CompareRopesAsBcds:
PROC[rope1,rope2:
ROPE, stats: Stats] =
TRUSTED {
bcdBuf1: Buffer = NEW[BufferRep[SIZE[BCD]]];
bcd1: BcdPtr = LOOPHOLE[@bcdBuf1[0]];
bcdBuf2: Buffer = NEW[BufferRep[SIZE[BCD]]];
bcd2: BcdPtr = LOOPHOLE[@bcdBuf2[0]];
len: INT = MIN[rope1.Length[], rope2.Length[]];
GetWords[rope1, 0, SIZE[BCD], bcdBuf1];
GetWords[rope2, 0, SIZE[BCD], bcdBuf2];
stats.base1 ← bcd1;
stats.base2 ← bcd2;
(stats.out).PutRope[stats.name1];
(stats.out).PutRope["\n version: "];
VersionOut[bcd1.version, stats, TRUE];
(stats.out).PutF[", bytes: %g\n source: ", [integer[rope1.Length[]]]];
NameOut[rope1, bcd1.ssOffset, bcd1.source, stats];
(stats.out).PutRope["\n sourceVersion: "];
VersionOut[bcd1.sourceVersion, stats];
(stats.out).PutRope["\n"];
(stats.out).PutRope[stats.name2];
(stats.out).PutRope["\n version: "];
VersionOut[bcd2.version, stats, TRUE];
(stats.out).PutF[", bytes: %g\n source: ", [integer[rope2.Length[]]]];
NameOut[rope2, bcd2.ssOffset, bcd2.source, stats];
(stats.out).PutRope["\n sourceVersion: "];
VersionOut[bcd2.sourceVersion, stats];
(stats.out).PutRope["\n"];
IF rope1.Length[] # rope2.Length[]
THEN {
(stats.out).PutRope["Warning, files have different lengths.\n"];
stats.errors ← stats.errors +1};
CompareEntries[
"In versionIdent\n",
@bcd1.versionIdent, @bcd2.versionIdent, SIZE[CARDINAL], stats];
CompareEntries[
"In version\n",
@bcd1.version, @bcd2.version, SIZE[VersionStamp], stats];
CompareEntries[
"In creator\n", @bcd1.creator, @bcd2.creator, SIZE[VersionStamp], stats];
CompareEntries[
"In sourceVersion\n",
@bcd1.sourceVersion, @bcd2.sourceVersion, SIZE[VersionStamp], stats];
CompareEntries[
"In source\n",
@bcd1.source, @bcd2.source, SIZE[NameRecord], stats];
CompareEntries[
"In nPages\n",
@bcd1.nConfigs-1, @bcd2.nConfigs-1, SIZE[CARDINAL], stats];
CompareEntries[
"In nConfigs\n",
@bcd1.nConfigs, @bcd2.nConfigs, SIZE[CARDINAL], stats];
CompareEntries[
"In nModules\n",
@bcd1.nModules, @bcd2.nModules, SIZE[CARDINAL], stats];
CompareEntries[
"In nImports\n",
@bcd1.nImports, @bcd2.nImports, SIZE[CARDINAL], stats];
CompareEntries[
"In options (definitions, repackaged, typeExported, tableCompiled, versions, extended)\n",
@bcd1.nDummies-1, @bcd2.nDummies-1, SIZE[CARDINAL], stats];
CompareEntries[
"In nDummies\n",
@bcd1.nDummies, @bcd2.nDummies, SIZE[CARDINAL], stats];
IF stats.debug THEN (stats.out).PutRope["** Tables\n"];
CompareTables[
"string table", @bcd1.ssOffset, @bcd2.ssOffset, stats];
CompareTables[
"config table", @bcd1.ctOffset, @bcd2.ctOffset, stats];
CompareTables[
"module table", @bcd1.mtOffset, @bcd2.mtOffset, stats];
CompareTables[
"import table", @bcd1.impOffset, @bcd2.impOffset, stats];
CompareTables[
"export table", @bcd1.expOffset, @bcd2.expOffset, stats];
CompareTables[
"external variable table", @bcd1.evOffset, @bcd2.evOffset, stats];
CompareTables[
"segment table", @bcd1.sgOffset, @bcd2.sgOffset, stats];
CompareTables[
"file table", @bcd1.ftOffset, @bcd2.ftOffset, stats];
CompareTables[
"space table", @bcd1.spOffset, @bcd2.spOffset, stats];
CompareTables[
"name table", @bcd1.ntOffset, @bcd2.ntOffset, stats];
CompareTables[
"type table", @bcd1.typOffset, @bcd2.typOffset, stats];
CompareTables[
"type map table", @bcd1.tmOffset, @bcd2.tmOffset, stats];
CompareTables[
"frame pack table", @bcd1.fpOffset, @bcd2.fpOffset, stats];
IF bcd1.extended
AND bcd2.extended
THEN {
CompareTables[
"link fragment table", @bcd1.lfOffset, @bcd2.lfOffset, stats, TRUE];
CompareTables[
"ref literal fragment table", @bcd1.rfOffset, @bcd2.rfOffset, stats, TRUE];
CompareTables[
"type fragment table", @bcd1.tfOffset, @bcd2.tfOffset, stats, TRUE];
CompareEntries[
"In rtPages\n", @bcd1.rtPages, @bcd2.rtPages, SIZE[CARDINAL], stats]};
IF stats.debug
THEN {
(stats.out).PutRope["** Segments for file 1\n"];
SegmentsOut[rope1, bcd1, stats];
(stats.out).PutRope["** Segments for file 2\n"];
SegmentsOut[rope2, bcd2, stats]};
IF stats.debug
THEN {
(stats.out).PutRope["** Files for file 1\n"];
FilesOut[rope1, bcd1, stats];
(stats.out).PutRope["** Files for file 2\n"];
FilesOut[rope2, bcd2, stats]};
{
-- compare the code in the various modules
mti1: CARDINAL ← LOOPHOLE[FIRST[BcdDefs.MTIndex], CARDINAL]+bcd1.mtOffset;
mti2: CARDINAL ← LOOPHOLE[FIRST[BcdDefs.MTIndex], CARDINAL]+bcd2.mtOffset;
FOR i:
NAT
IN [0..
MIN[bcd1.nModules,bcd2.nModules])
DO
mtr1: BcdDefs.MTRecord;
mtr2: BcdDefs.MTRecord;
sgr1: BcdDefs.SGRecord;
sgr2: BcdDefs.SGRecord;
nw1,nw2,sgi1,sgi2: CARDINAL;
byteOff1,byteOff2: INT;
sb1,sb2: INT;
GetWordsToPtr[rope1, mti1, SIZE[BcdDefs.MTRecord], @mtr1];
GetWordsToPtr[rope2, mti2, SIZE[BcdDefs.MTRecord], @mtr2];
WITH m~~mtr1
SELECT
FROM
direct => nw1 ← SIZE[BcdDefs.MTRecord[direct]] + m.length*SIZE[BcdDefs.Link];
indirect => nw1 ← SIZE[BcdDefs.MTRecord[indirect]];
multiple => nw1 ← SIZE[BcdDefs.MTRecord[multiple]];
ENDCASE => ERROR;
WITH m~~mtr2
SELECT
FROM
direct => nw2 ← SIZE[BcdDefs.MTRecord[direct]] + m.length*SIZE[BcdDefs.Link];
indirect => nw2 ← SIZE[BcdDefs.MTRecord[indirect]];
multiple => nw2 ← SIZE[BcdDefs.MTRecord[multiple]];
ENDCASE => ERROR;
IF stats.doCode
OR stats.doAll
THEN {
Compare the code portions of this module.
First, get the proper SGRecords for the code
sgi1 ← bcd1.sgOffset + LOOPHOLE[mtr1.code.sgi, CARDINAL];
sgi2 ← bcd2.sgOffset + LOOPHOLE[mtr2.code.sgi, CARDINAL];
GetWordsToPtr[rope1, sgi1, SIZE[BcdDefs.SGRecord], @sgr1];
GetWordsToPtr[rope2, sgi2, SIZE[BcdDefs.SGRecord], @sgr2];
sb1 ← (sgr1.base-1)*INT[wordsPerPage];
sb2 ← (sgr2.base-1)*INT[wordsPerPage];
byteOff1 ← (mtr1.code.offset+sb1)*bytesPerWord;
byteOff2 ← (mtr2.code.offset+sb2)*bytesPerWord;
SELECT
TRUE
FROM
mtr1.code.length = 0
AND mtr2.code.length = 0 =>
(stats.out).PutRope["No code.\n"];
mtr1.code.length = 0 =>
(stats.out).PutF[
"No code for file 1, %g bytes for file 2.\n",
[integer[mtr2.code.length]]];
mtr1.code.length = 0 =>
(stats.out).PutF[
"No code for file 2, %g bytes for file 1.\n",
[integer[mtr1.code.length]]];
ENDCASE =>
Finally, compare the code
CompareBytes[
IO.PutFR[
"In code for %g (module %g)\n",
[rope[GetName[rope1, bcd1.ssOffset, mtr1.name]]],
[integer[i]]],
byteOff1, byteOff2, MIN[mtr1.code.length, mtr2.code.length], stats];
};
IF stats.doSymbols
OR stats.doAll
THEN {
Compare the symbol portions of this module.
First, get the proper SGRecords for the symbols
sgi1 ← bcd1.sgOffset + LOOPHOLE[mtr1.sseg, CARDINAL];
sgi2 ← bcd2.sgOffset + LOOPHOLE[mtr2.sseg, CARDINAL];
GetWordsToPtr[rope1, sgi1, SIZE[BcdDefs.SGRecord], @sgr1];
GetWordsToPtr[rope2, sgi2, SIZE[BcdDefs.SGRecord], @sgr2];
sb1 ← (sgr1.base-1)*INT[wordsPerPage];
sb2 ← (sgr2.base-1)*INT[wordsPerPage];
Set the symbol offset in stats for use by Diagnose
stats.symOff1 ← sb1;
stats.symOff2 ← sb2;
Compare the basic symbols
CompareWords[
IO.PutFR[
"In basic symbols for %g (module %g)\n",
[rope[GetName[rope1, bcd1.ssOffset, mtr1.name]]], [integer[i]]],
sb1, sb2, MIN[sgr1.pages, sgr2.pages]*wordsPerPage, stats];
Compare the extended symbols
stats.symOff1 ← stats.symOff2 ← 0; -- inhibit Diagnose
CompareWords[
IO.PutFR[
"In extended symbols for %g (module %g)\n",
[rope[GetName[rope1, bcd1.ssOffset, mtr1.name]]],
[integer[i]]],
sb1+sgr1.pages*wordsPerPage,
sb2+sgr2.pages*wordsPerPage,
MIN[sgr1.extraPages, sgr2.extraPages]*wordsPerPage,
stats
];
};
mti1 ← mti1 + nw1;
mti2 ← mti2 + nw2;
ENDLOOP;
};
IF stats.debug
THEN {
stats.maxTabLim1 ← stats.maxTabLim2 ← SIZE[BCD];
CompareWords[
"In whole file\n",
stats.maxTabLim1, stats.maxTabLim2,
MIN[len/bytesPerWord-stats.maxTabLim1, len/bytesPerWord-stats.maxTabLim2],
stats];
};
SELECT stats.errors
FROM
0 => {};
1 => ERROR GiveUp["Too bad, only one little difference."];
ENDCASE =>
ERROR GiveUp[
IO.PutFR["%g differences encountered.", [integer[stats.errors]]]];
};
CompareWords:
PROC
[name: ROPE, offset1,offset2: INT, nWords: INT, stats: Stats] = TRUSTED {
r1: ROPE = (stats.rope1).Substr[0, (offset1+nWords)*bytesPerWord];
lim1: INT = r1.Length[];
r2: ROPE = (stats.rope2).Substr[0, (offset2+nWords)*bytesPerWord];
lim2: INT = r2.Length[];
sth1,sth2: SymbolTableHeader ← NIL;
off1: INT ← offset1*bytesPerWord;
off2: INT ← offset2*bytesPerWord;
IF stats.debug
THEN
(stats.out).PutF[
"** %g CompareWords[offset1: %g, offset2: %g, nWords: %g]\n",
[rope[name]], [integer[offset1]], [integer[offset2]], [integer[nWords]]];
WHILE off1 < lim1
AND off2 < lim2
DO
nc: INT = r1.Run[off1, r2, off2];
nOff1: INT ← off1+nc;
nOff2: INT ← off2+nc;
nwOff1,nwOff2: INT;
w1,w2: Word;
f1,f2: ROPE;
which1,which2: ROPE ← NIL;
IF nOff1 >= lim1 OR nOff2 >= lim2 THEN EXIT;
IF name # NIL THEN {(stats.out).PutRope[name]; name ← NIL};
nwOff1 ← nOff1/bytesPerWord;
nwOff2 ← nOff2/bytesPerWord;
f1 ← r1.Flatten[nOff1 ← nwOff1*bytesPerWord, bytesPerWord];
w1 ← (LOOPHOLE[f1, WordPtr]+SIZE[TEXT])^;
f2 ← r2.Flatten[nOff2 ← nwOff2*bytesPerWord, bytesPerWord];
w2 ← (LOOPHOLE[f2, WordPtr]+SIZE[TEXT])^;
IF nOff1 = nOff2 THEN (stats.out).PutF[" at word offset %g", [integer[nwOff1]]]
ELSE (stats.out).PutF[
" at word offsets (%g, %g)", [integer[nwOff1]], [integer[nwOff2]]];
(stats.out).PutF[
", %b # %b (\"%q\" # \"%q\")\n",
[cardinal[w1]], [cardinal[w2]], [rope[f1]], [rope[f2]]];
IF stats.symOff1 > 0
THEN {
IF sth1 =
NIL
THEN {
sth1 ← NEW[SymbolSegment.STHeader];
GetWordsToPtr[
stats.rope1, stats.symOff1, SIZE[SymbolSegment.STHeader], LOOPHOLE[sth1]]
};
Try to determine which block it is in
which1 ← Diagnose[stats.rope1, sth1, offset1, nwOff1];
(stats.out).PutRope[which1];
(stats.out).PutRope["\n"]};
IF stats.symOff2 > 0
THEN {
IF sth2 =
NIL
THEN {
sth2 ← NEW[SymbolSegment.STHeader];
GetWordsToPtr[
stats.rope2, stats.symOff2, SIZE[SymbolSegment.STHeader], LOOPHOLE[sth2]];
};
Try to determine which block it is in
which2 ← Diagnose[stats.rope2, sth2, offset2, nwOff2];
IF
NOT which1.Equal[which2]
THEN {
Different diagnoses for the two files!
(stats.out).PutRope[" (bcd1)\n"];
(stats.out).PutRope[which2];
(stats.out).PutRope[" (bcd2)\n"]};
};
IF (stats.errors ← stats.errors + 1) > stats.maxErrors
THEN {
ERROR GiveUp["Too many differences encountered."]};
off1 ← nOff1 + bytesPerWord;
off2 ← nOff2 + bytesPerWord;
ENDLOOP;
};
CompareTables:
PROC
[name: ROPE, ptr1,ptr2: LONG POINTER, stats: Stats, rt: BOOL ← FALSE] = TRUSTED {
src1: WordPtr ← LOOPHOLE[ptr1, WordPtr];
offset1: CARDINAL ← LOOPHOLE[src1, WordPtr]^;
len1: CARDINAL = (src1+1)^;
lim1: CARDINAL = len1+offset1;
src2: WordPtr ← LOOPHOLE[ptr2, WordPtr];
offset2: CARDINAL ← src2^;
len2: CARDINAL = (src2+1)^;
lim2: CARDINAL = len2+offset2;
nWords: CARDINAL = MIN[lim1, lim2];
oldErrors: CARDINAL;
rtOff1,rtOff2: INT ← 0;
IF stats.debug
THEN {
(stats.out).PutF[
" %g, range1 = [%g, %g), range2 = [%g, %g).\n",
[rope[name]], [integer[offset1]], [integer[lim1]], [integer[offset2]], [integer[lim2]]];
};
stats.maxTabLim1 ← MAX[stats.maxTabLim1, lim1];
stats.maxTabLim2 ← MAX[stats.maxTabLim2, lim2];
IF offset1 # offset2
THEN {
(stats.out).PutF[
"Offsets for %g not equal, %g # %g\n",
[rope[name]], [cardinal[offset1]], [cardinal[offset2]]];
IF (stats.errors ← stats.errors + 1) > stats.maxErrors
THEN {
ERROR GiveUp["Too many differences encountered."]};
};
IF len1 # len2
THEN {
(stats.out).PutF[
"Lengths for %g not equal, %g # %g\n",
[rope[name]], [cardinal[len1]], [cardinal[len2]]];
IF (stats.errors ← stats.errors + 1) > stats.maxErrors
THEN {
ERROR GiveUp["Too many differences encountered."]};
};
oldErrors ← stats.errors;
IF rt
THEN {
This table should be interpreted relative to the RTBcd extension
rtOff1 ← LOOPHOLE[stats.base1, BcdPtr].rtPages.relPageBase*wordsPerPage;
rtOff2 ← LOOPHOLE[stats.base2, BcdPtr].rtPages.relPageBase*wordsPerPage};
Now compare the words within the table.
CompareWords[
Rope.Cat["In ", name, "\n"],
rtOff1+offset1, rtOff2+offset2, MIN[len1, len2], stats
! GiveUp => {
stats.errors ← oldErrors+1;
(stats.out).PutRope["... too many differences for this table.\n"];
CONTINUE}];
};