<> <> <> <> <> <> DIRECTORY Basics: TYPE USING [BITXOR, bytesPerWord, LongMult, LowHalf, RawBytes], BcdDefs: TYPE USING [Base, BcdBase, FTIndex, FTNull, FTRecord, --PageSize,-- SGIndex, VersionStamp], BcdLiterals: TYPE USING [], IO: TYPE USING [STREAM, UnsafePutBlock], PrincOpsUtils: TYPE USING [LongCopy], RCMapOps: TYPE USING [RCMT, MapMap, Create, Destroy, FindMapMapEntry, GetSpan, Include], RTBcd: TYPE USING [AnyStamp, RefLitIndex, RefLitItem, RefLitList, RTBase, RTHeader, StampIndex, StampList, TypeIndex, TypeItem, TypeList, UTInfo, VersionID], UnsafeStorage: TYPE USING [GetSystemUZone], VM: TYPE USING [AddressForPageNumber, Allocate, Free, Interval, nullInterval, wordsPerPage]; BcdLiteralsImpl: MONITOR IMPORTS Basics, IO, PrincOpsUtils, RCMapOps, UnsafeStorage, VM EXPORTS BcdLiterals = { OPEN BcdDefs; <> TypeMap: TYPE ~ RECORD[SEQUENCE length: NAT OF RTBcd.TypeIndex]; LitMap: TYPE ~ RECORD[SEQUENCE length: CARDINAL OF RTBcd.RefLitIndex]; <> Relation: TYPE ~ {ls, gr, eq}; Branch: TYPE ~ CARDINAL --[0..NAT.LAST+1]--; nullBranch: Branch ~ NAT.LAST+1; Nodes: TYPE ~ RECORD[SEQUENCE length: NAT OF RECORD [l, r: Branch]]; AdjustNodes: PROC[tree: REF Nodes, newLimit: NAT] RETURNS[newTree: REF Nodes] ~ { oldLimit: NAT ~ IF tree = NIL THEN 0 ELSE tree.length; newTree _ NEW[Nodes[newLimit]]; IF tree # NIL THEN { FOR i: NAT IN [0 .. MIN[oldLimit, newLimit]) DO newTree[i] _ tree[i] ENDLOOP; }; }; Scramble: PROC[n: CARDINAL] RETURNS[WORD] ~ INLINE { -- see Knuth, v 3, p. 509-511 RETURN[Basics.LowHalf[Basics.LongMult[n, 44451]]]}; <> HVIndex: TYPE ~ [0 .. 251); HTIndex: TYPE ~ CARDINAL; HTNull: HTIndex ~ HTIndex.LAST; HashNode: TYPE ~ RECORD [offset: CARDINAL, link: HTIndex]; HashSeq: TYPE ~ RECORD [SEQUENCE length: CARDINAL OF HashNode]; <> LiteralState: TYPE ~ REF LiteralStateInfo; LiteralStateInfo: PUBLIC TYPE~RECORD[ <> typeMap: REF TypeMap_NIL, litMap: REF LitMap_NIL, <> typeList: REF RTBcd.TypeList_NIL, nextType: NAT_0, typeTree: REF Nodes_NIL, <> litList: REF RTBcd.RefLitList_NIL, nextLit: NAT_0, litTree: REF Nodes_NIL, <> rcmt: RCMapOps.RCMT_NIL, <> textSpace: VM.Interval_VM.nullInterval, textBase: LONG POINTER_NIL, textOffset, textLimit: CARDINAL_0, textPages: CARDINAL_0, hashVec: REF ARRAY HVIndex OF HTIndex_NIL, ht: REF HashSeq_NIL, nextHti: HTIndex_0, <> stampList: REF RTBcd.StampList_NIL, nextStamp: NAT_1 ]; <<>> <> Copy: PROC[from: LONG POINTER, nwords: CARDINAL, to: LONG POINTER] ~ PrincOpsUtils.LongCopy; zone: UNCOUNTED ZONE = UnsafeStorage.GetSystemUZone[]; <> <<>> Load: PUBLIC PROC[ ls: LiteralState, bcdBase: BcdDefs.BcdBase, MapFile: PROC[FTIndex] RETURNS[FTIndex], MapSegment: PROC[SGIndex] RETURNS[SGIndex]] RETURNS[success: BOOL _ TRUE] ~ { IF bcdBase.rtPages.pages # 0 THEN { ftb: BcdDefs.Base ~ LOOPHOLE[bcdBase, BcdDefs.Base] + bcdBase.ftOffset; ftLimit: FTIndex ~ bcdBase.ftLimit; VersionToFile: PROC[i: RTBcd.StampIndex] RETURNS[fti: BcdDefs.FTIndex] ~ INLINE { FOR fti _ FTIndex.FIRST, fti + FTRecord.SIZE UNTIL fti = ftLimit DO IF ls.stampList[i] = ftb[fti].version THEN RETURN; ENDLOOP; RETURN[FTNull]}; rcmMap: RCMapOps.MapMap; MapTypeItem: PROC[old: RTBcd.TypeItem, name: LONG POINTER TO TEXT] RETURNS[RTBcd.TypeItem] ~ { stamp: RTBcd.StampIndex ~ IF old.ut.version = RTBcd.AnyStamp THEN RTBcd.AnyStamp ELSE EnterStamp[rtHeader[rtHeader.stampTable][old.ut.version]]; IF ~old.canonical AND stamp # RTBcd.AnyStamp THEN [] _ MapFile[VersionToFile[stamp]]; -- force file entry RETURN[[ table~MapSegment[old.table], sei~old.sei, canonical~old.canonical, ct~[EnterText[name]], ut~[version: stamp, sei: old.ut.sei], rcMap~rcmMap.FindMapMapEntry[old.rcMap]]]}; MapLitItem: PROC[old: RTBcd.RefLitItem, lit: LONG POINTER TO TEXT] RETURNS[RTBcd.RefLitItem] ~ { RETURN[[ referentType~MapType[ls, old.referentType], offset~EnterText[lit], length~old.length]]}; EnterType: PROC[item: RTBcd.TypeItem] RETURNS[index: RTBcd.TypeIndex] ~ { i: Branch _ 0; InsertType: PROC[item: RTBcd.TypeItem] RETURNS[index: Branch] ~ { IF ls.typeList = NIL OR ls.nextType >= ls.typeList.length THEN { oldLimit: NAT ~ IF ls.typeList = NIL THEN 0 ELSE ls.typeList.length; newLimit: NAT ~ oldLimit + MAX[MIN[oldLimit/2, 500], 50]; AdjustTypeList[ls, newLimit]; ls.typeTree _ AdjustNodes[ls.typeTree, newLimit]}; index _ ls.nextType; ls.nextType _ ls.nextType + 1; ls.typeList[index] _ item; ls.typeTree[index] _ [l~nullBranch, r~nullBranch]; RETURN}; IF ls.nextType = 0 THEN [] _ InsertType[item]; DO SELECT CompareTypes[item, ls.typeList[i]] FROM $ls => { IF ls.typeTree[i].l = nullBranch THEN ls.typeTree[i].l _ InsertType[item]; i _ ls.typeTree[i].l}; $gr => { IF ls.typeTree[i].r = nullBranch THEN ls.typeTree[i].r _ InsertType[item]; i _ ls.typeTree[i].r}; ENDCASE => RETURN[[i]] ENDLOOP}; EnterRefLit: PROC[item: RTBcd.RefLitItem] RETURNS[RTBcd.RefLitIndex] ~ { i: Branch _ 0; InsertRefLit: PROC[item: RTBcd.RefLitItem] RETURNS[index: Branch] ~ { IF ls.litList = NIL OR ls.nextLit >= ls.litList.length THEN { oldLimit: NAT ~ IF ls.litList = NIL THEN 0 ELSE ls.litList.length; newLimit: NAT ~ oldLimit + MAX[MIN[oldLimit/2, 500], 50]; AdjustLitList[ls, newLimit]; ls.litTree _ AdjustNodes[ls.litTree, newLimit]}; index _ ls.nextLit; ls.nextLit _ ls.nextLit + 1; ls.litList[index] _ item; ls.litTree[index] _ [l~nullBranch, r~nullBranch]; RETURN}; IF ls.nextLit = 0 THEN [] _ InsertRefLit[item]; DO SELECT CompareLits[item, ls.litList[i]] FROM $ls => { IF ls.litTree[i].l = nullBranch THEN ls.litTree[i].l _ InsertRefLit[item]; i _ ls.litTree[i].l}; $gr => { IF ls.litTree[i].r = nullBranch THEN ls.litTree[i].r _ InsertRefLit[item]; i _ ls.litTree[i].r}; ENDCASE => RETURN[[i]] ENDLOOP}; LitText: PROC[offset: CARDINAL] RETURNS[LONG POINTER TO TEXT] ~ INLINE { RETURN[ls.textBase + offset]}; EnterText: PROC[s: LONG POINTER TO TEXT] RETURNS[offset: CARDINAL] ~ { HashValue: PROC[s: LONG POINTER TO TEXT] RETURNS[HVIndex] ~ INLINE { n: CARDINAL ~ s.length; v: WORD ~ (IF n = 0 THEN 0 ELSE (s[0]-0c)*177b + (s[n-1]-0c)); RETURN[Basics.BITXOR[v, n*17b] MOD ls.hashVec^.LENGTH]}; hvi: HVIndex ~ HashValue[s]; hti: HTIndex; nw: CARDINAL; ExpandTextSpace: PROC ~ INLINE { newPages: CARDINAL ~ ls.textPages + MAX[MIN[ls.textPages/2, 16], 4]; newSpace: VM.Interval ~ VM.Allocate[count~newPages]; newBase: LONG POINTER ~ VM.AddressForPageNumber[newSpace.page]; IF ls.textSpace # VM.nullInterval THEN { Copy[from~ls.textBase, to~newBase, nwords~ls.textOffset]; VM.Free[ls.textSpace]}; ls.textSpace _ newSpace; ls.textBase _ newBase; ls.textPages _ newPages; ls.textLimit _ newPages*VM.wordsPerPage}; FOR hti _ ls.hashVec[hvi], ls.ht[hti].link UNTIL hti = HTNull DO t: LONG POINTER TO TEXT ~ LitText[ls.ht[hti].offset]; IF EqText[s, t] THEN RETURN[ls.ht[hti].offset]; ENDLOOP; nw _ TEXT[s.length].SIZE; WHILE ls.textOffset + nw > ls.textLimit DO ExpandTextSpace[] ENDLOOP; offset _ ls.textOffset; Copy[from~s, to~ls.textBase+ls.textOffset, nwords~nw]; ls.textOffset _ ls.textOffset + nw; hti _ AllocateHash[]; ls.ht[hti] _ [link~ls.hashVec[hvi], offset~offset]; ls.hashVec[hvi] _ hti; RETURN}; AllocateHash: PROC RETURNS[hti: HTIndex] ~ { ExpandHashSpace: PROC ~ { oldLength: CARDINAL ~ IF ls.ht = NIL THEN 0 ELSE ls.ht.length; newLength: CARDINAL ~ oldLength + MAX[MIN[oldLength/2, 1024], 256]; newHt: REF HashSeq ~ NEW[HashSeq[newLength]]; IF ls.ht # NIL THEN FOR i: NAT IN [0 .. ls.ht.length) DO newHt[i] _ ls.ht[i] ENDLOOP; ls.ht _ newHt}; IF ls.ht = NIL OR ls.nextHti >= ls.ht.length THEN ExpandHashSpace[]; hti _ ls.nextHti; ls.nextHti _ ls.nextHti + 1; RETURN}; EnterStamp: PROC[stamp: BcdDefs.VersionStamp] RETURNS[index: RTBcd.StampIndex] ~ { ExpandStampList: PROC ~ INLINE { oldSize: NAT ~ IF ls.stampList = NIL THEN 0 ELSE ls.stampList.limit-1; AdjustStampList[ls, oldSize + MAX[MIN[oldSize/2, 256], 64]]}; FOR i: NAT IN [1 .. ls.nextStamp) DO IF stamp = ls.stampList[i] THEN RETURN[[i]]; ENDLOOP; IF ls.stampList = NIL OR ls.nextStamp >= ls.stampList.limit THEN ExpandStampList[]; index _ [ls.nextStamp]; ls.stampList[ls.nextStamp] _ stamp; ls.nextStamp _ ls.nextStamp + 1; RETURN}; rtHeader: RTBcd.RTBase ~ LOOPHOLE[bcdBase + bcdBase.rtPages.relPageBase*VM.wordsPerPage--BcdDefs.PageSize--]; nTypes, nLits: NAT; IF rtHeader.versionIdent # RTBcd.VersionID THEN GO TO badFormat; nTypes _ rtHeader[rtHeader.typeTable].length; IF nTypes # 0 THEN { rcmMap _ ls.rcmt.Include[ rcmb~@rtHeader[rtHeader.rcMapBase], size~rtHeader.rcMapLength, zone~zone]; ls.typeMap _ NEW[TypeMap[nTypes]]; FOR i: NAT IN [0 .. nTypes) DO typeString: LONG POINTER TO TEXT ~ @rtHeader[rtHeader.litBase] + rtHeader[rtHeader.typeTable][i].ct; ls.typeMap[i] _ EnterType[MapTypeItem[rtHeader[rtHeader.typeTable][i], typeString]]; ENDLOOP; IF rcmMap # NIL THEN zone.FREE[@rcmMap]}; nLits _ rtHeader[rtHeader.refLitTable].length; IF nLits # 0 THEN { ls.litMap _ NEW[LitMap[nLits]]; FOR i: NAT IN [0 .. rtHeader[rtHeader.refLitTable].length) DO pName: LONG POINTER TO TEXT ~ @rtHeader[rtHeader.litBase] + rtHeader[rtHeader.refLitTable][i].offset; ls.litMap[i] _ EnterRefLit[MapLitItem[rtHeader[rtHeader.refLitTable][i], pName]]; ENDLOOP}; EXITS badFormat => success _ FALSE; }; RETURN}; MapLitLink: PUBLIC PROC[ls: LiteralState, old: RTBcd.RefLitIndex] RETURNS[RTBcd.RefLitIndex] ~ { <> RETURN[IF ls.litMap = NIL THEN ERROR ELSE MapLit[ls, old]]}; MapTypeLink: PUBLIC PROC[ls: LiteralState, old: RTBcd.TypeIndex] RETURNS[RTBcd.TypeIndex] ~ { <> RETURN[IF ls.typeMap = NIL THEN ERROR ELSE MapType[ls, old]]}; Unload: PUBLIC PROC[ls: LiteralState] ~ { ls.typeMap _ NIL; ls.litMap _ NIL}; <> MapType: PROC[ls: LiteralState, old: RTBcd.TypeIndex] RETURNS[RTBcd.TypeIndex] ~ INLINE { RETURN[ls.typeMap[old]]}; CompareTypes: PROC[l, r: RTBcd.TypeItem] RETURNS[Relation] ~ { sl: WORD ~ Scramble[l.ct]; sr: WORD ~ Scramble[r.ct]; RETURN[ SELECT sl FROM < sr => $ls, > sr => $gr, ENDCASE => SELECT TRUE FROM l.canonical AND ~r.canonical => $ls, ~l.canonical AND r.canonical => $gr, ENDCASE => -- l.canonical = r.canonical IF l.canonical THEN $eq ELSE CompareUTFs[l.ut, r.ut]] }; CompareUTFs: PROC[l, r: RTBcd.UTInfo] RETURNS[Relation] ~ { UTWords: TYPE ~ ARRAY [0..RTBcd.UTInfo.SIZE) OF WORD; FOR i: NAT IN [0..RTBcd.UTInfo.SIZE) DO SELECT LOOPHOLE[l, UTWords][i] FROM < LOOPHOLE[r, UTWords][i] => RETURN[$ls]; > LOOPHOLE[r, UTWords][i] => RETURN[$gr]; ENDCASE; ENDLOOP; RETURN[$eq]}; AdjustTypeList: PROC[ls: LiteralState, newLimit: NAT] ~ { oldLimit: NAT ~ IF ls.typeList = NIL THEN 0 ELSE ls.typeList.length; newList: REF RTBcd.TypeList ~ NEW[RTBcd.TypeList[newLimit]]; IF ls.typeList # NIL THEN { FOR i: NAT IN [0 .. MIN[oldLimit, newLimit]) DO newList[i] _ ls.typeList[i] ENDLOOP; ls.typeList _ NIL}; ls.typeList _ newList}; <> MapLit: PROC[ls: LiteralState, old: RTBcd.RefLitIndex] RETURNS[RTBcd.RefLitIndex] ~ INLINE { RETURN[ls.litMap[old]]}; CompareLits: PROC[l, r: RTBcd.RefLitItem] RETURNS[Relation] ~ { sl: WORD ~ Scramble[l.offset]; sr: WORD ~ Scramble[r.offset]; RETURN[SELECT sl FROM < sr => $ls, > sr => $gr, ENDCASE => SELECT l.length FROM = r.length => SELECT l.referentType - r.referentType FROM = 0 => $eq, > 0 => $gr, ENDCASE => $ls, < r.length => $ls, ENDCASE => $gr] }; AdjustLitList: PROC[ls: LiteralState, newLimit: NAT] ~ { oldLimit: NAT ~ IF ls.litList = NIL THEN 0 ELSE ls.litList.length; newList: REF RTBcd.RefLitList ~ NEW[RTBcd.RefLitList[newLimit]]; IF ls.litList # NIL THEN { FOR i: NAT IN [0 .. MIN[oldLimit, newLimit]) DO newList[i] _ ls.litList[i] ENDLOOP; ls.litList _ NIL}; ls.litList _ newList}; <> OpenRCMap: PROC[ls: LiteralState] ~ { IF ls.rcmt = NIL THEN ls.rcmt _ RCMapOps.Create[zone~zone, nPages~0, ptr~NIL, expansionOK~TRUE]; }; CloseRCMap: PROC[ls: LiteralState] ~ { IF ls.rcmt # NIL THEN ls.rcmt _ RCMapOps.Destroy[ls.rcmt]; }; <> EqText: PROC[t1, t2: LONG POINTER TO TEXT] RETURNS [BOOL] ~ INLINE { IF t1.length # t2.length THEN RETURN[FALSE]; FOR i: NAT IN [0..t1.length) DO IF t1[i] # t2[i] THEN RETURN[FALSE] ENDLOOP; RETURN[TRUE]}; <> AdjustStampList: PROC[ls: LiteralState, newSize: NAT] ~ { oldSize: NAT ~ IF ls.stampList = NIL THEN 0 ELSE ls.stampList.limit-1; newList: REF RTBcd.StampList ~ NEW[RTBcd.StampList[newSize]]; FOR i: NAT IN [1 .. MIN[oldSize, newSize]] DO newList[i] _ ls.stampList[i] ENDLOOP; ls.stampList _ newList}; <> EnterVersionFiles: PUBLIC PROC[ ls: LiteralState, ftb: BcdDefs.Base, ftLimit: BcdDefs.FTIndex, MapFile: PROC[BcdDefs.FTIndex] RETURNS[BcdDefs.FTIndex]] ~ { VersionToFile: PROC[i: RTBcd.StampIndex] RETURNS[fti: BcdDefs.FTIndex] ~ { FOR fti _ FTIndex.FIRST, fti + FTRecord.SIZE UNTIL fti = ftLimit DO IF ls.stampList[i] = ftb[fti].version THEN RETURN; ENDLOOP; RETURN[FTNull]}; FOR i: NAT IN [0 .. ls.nextType) DO IF ~ls.typeList[i].canonical AND ls.typeList[i].ut.version # RTBcd.AnyStamp THEN [] _ MapFile[VersionToFile[ls.typeList[i].ut.version]]; ENDLOOP }; RTHeaderSize: CARDINAL ~ RTBcd.RTHeader.SIZE; SegmentSize: PUBLIC PROC[ls: LiteralState] RETURNS[nWords: CARDINAL] ~ { RETURN[IF ls.litList = NIL AND ls.typeList = NIL THEN 0 ELSE RTHeaderSize + RTBcd.RefLitList[ls.nextLit].SIZE + ls.textOffset + RTBcd.TypeList[ls.nextType].SIZE + RTBcd.StampList[ls.nextStamp-1].SIZE + ls.rcmt.GetSpan[].size] }; UpdateSegments: PUBLIC PROC[ls: LiteralState, MapSegment: PROC[SGIndex] RETURNS[SGIndex]] ~ { <> FOR i: NAT IN [0 .. ls.nextType) DO ls.typeList[i].table _ MapSegment[ls.typeList[i].table] ENDLOOP }; SealLiterals: PUBLIC PROC[ls: LiteralState] ~ { ls.hashVec _ NIL; ls.ht _ NIL; ls.litTree _ NIL; ls.typeTree _ NIL}; Write: PUBLIC PROC[ls: LiteralState, stream: IO.STREAM] ~ { IF ls.litList # NIL OR ls.typeList # NIL THEN { bytesPerWord: CARDINAL ~ Basics.bytesPerWord; litSize: CARDINAL ~ RTBcd.RefLitList[ls.nextLit].SIZE; typeSize: CARDINAL ~ RTBcd.TypeList[ls.nextType].SIZE; stampSize: CARDINAL ~ RTBcd.StampList[ls.nextStamp-1].SIZE; rcmSize: CARDINAL ~ ls.rcmt.GetSpan[].size; header: RTBcd.RTHeader _ [ refLitTable~LOOPHOLE[RTHeaderSize], litBase~LOOPHOLE[RTHeaderSize + litSize], litLength~ls.textOffset, rcMapBase~LOOPHOLE[(RTHeaderSize + litSize + ls.textOffset).LONG], rcMapLength~rcmSize, stampTable~LOOPHOLE[RTHeaderSize + litSize + ls.textOffset + rcmSize], typeTable~LOOPHOLE[RTHeaderSize + litSize + ls.textOffset + rcmSize + stampSize]]; stream.UnsafePutBlock[[LOOPHOLE[(@header).LONG, LONG POINTER TO Basics.RawBytes], 0, RTHeaderSize*bytesPerWord]]; AdjustLitList[ls, ls.nextLit]; stream.UnsafePutBlock [[LOOPHOLE[ls.litList, LONG POINTER TO Basics.RawBytes], 0, litSize*bytesPerWord]]; ls.litList _ NIL; IF ls.textSpace # VM.nullInterval THEN { stream.UnsafePutBlock[[ls.textBase, 0, ls.textOffset*bytesPerWord]]; VM.Free[ls.textSpace]}; IF rcmSize # 0 THEN stream.UnsafePutBlock[[ls.rcmt.GetSpan[].base, 0, rcmSize*bytesPerWord]]; AdjustStampList[ls, ls.nextStamp-1]; stream.UnsafePutBlock [[LOOPHOLE[ls.stampList, LONG POINTER TO Basics.RawBytes], 0, stampSize*bytesPerWord]]; ls.stampList _ NIL; AdjustTypeList[ls, ls.nextType]; stream.UnsafePutBlock [[LOOPHOLE[ls.typeList, LONG POINTER TO Basics.RawBytes], 0, typeSize*bytesPerWord]]; ls.typeList _ NIL} }; Create: PUBLIC PROC RETURNS[ls: LiteralState] ~ { ls _ NEW[LiteralStateInfo_[]]; ls.hashVec _ NEW[ARRAY HVIndex OF HTIndex _ ALL[HTNull]]; OpenRCMap[ls]; RETURN}; Finalize: PUBLIC PROC[ls: LiteralState] ~ { CloseRCMap[ls]; ls.hashVec _ NIL}; }.