DIRECTORY ImagerFontCache, Rope, RopeReader; ImagerFontCacheImpl: CEDAR MONITOR IMPORTS Rope, RopeReader EXPORTS ImagerFontCache = BEGIN FontCache: PUBLIC TYPE ~ REF FontCacheRep; FontCacheRep: PUBLIC TYPE ~ RECORD [ firstFontCode: NAT, seq: REF Seq ]; Seq: TYPE ~ RECORD [ SEQUENCE length: NAT OF REF Array ]; Array: TYPE ~ RECORD [ bc: CARDINAL, seq: SEQUENCE length: NAT OF REF ]; FontCode: TYPE ~ CARDINAL; FontObject: TYPE ~ ImagerFontCache.FontObject; FontObjectRep: TYPE ~ ImagerFontCache.FontObjectRep; fontCodeTableHeaders: NAT ~ 101; fontCodeTable: REF FontCodeTableRep _ NEW[FontCodeTableRep]; FontCodeTableRep: TYPE ~ ARRAY [0..fontCodeTableHeaders) OF CollisionList; CollisionList: TYPE ~ REF CollisionListItem; CollisionListItem: TYPE ~ RECORD [ next: CollisionList, fontCode: FontCode, fontObject: FontObject ]; fontCodeInverse: REF FontCodeInverseRep; FontCodeInverseRep: TYPE ~ RECORD [SEQUENCE length: NAT OF CollisionList]; nextFontCode: CARDINAL _ 0; Create: PUBLIC PROC RETURNS [FontCache] ~ { RETURN [NEW[FontCacheRep]]; }; GetFontCode: PUBLIC ENTRY PROC [fontObjectRep: FontObjectRep] RETURNS [fontCode: FontCode] ~ TRUSTED { ENABLE UNWIND => NULL; p: POINTER TO CARDINAL _ LOOPHOLE[@fontObjectRep]; hash: CARDINAL _ 0; IF SIZE[FontObjectRep] MOD SIZE[CARDINAL] # 0 THEN ERROR; THROUGH [0..SIZE[FontObjectRep]/SIZE[CARDINAL]) DO t: CARDINAL _ 2*hash; hash _ hash + hash*2*2 + p^; -- Depend on overflow not to raise a signal; p _ p + SIZE[CARDINAL]; ENDLOOP; hash _ hash MOD fontCodeTableHeaders; FOR c: CollisionList _ fontCodeTable[hash], c.next UNTIL c=NIL DO IF c.fontObject^ = fontObjectRep THEN RETURN [c.fontCode]; ENDLOOP; fontCodeTable[hash] _ NEW[CollisionListItem _ [fontCodeTable[hash], nextFontCode, NEW[FontObjectRep _ fontObjectRep]]]; IF fontCodeInverse = NIL OR fontCodeInverse.length <= nextFontCode THEN { old: REF FontCodeInverseRep _ fontCodeInverse; newLength: NAT _ IF old = NIL THEN 6 ELSE old.length + old.length/3 + 1; fontCodeInverse _ NEW[FontCodeInverseRep[newLength]]; IF old # NIL THEN FOR i: NAT IN [0..old.length) DO fontCodeInverse[i] _ old[i]; ENDLOOP; old _ NIL; }; fontCodeInverse[nextFontCode] _ fontCodeTable[hash]; fontCode _ nextFontCode; nextFontCode _ nextFontCode + 1; }; InterpretFontCode: PUBLIC ENTRY PROC [fontCode: FontCode] RETURNS [FontObject] ~ { ENABLE UNWIND => NULL; IF fontCodeInverse = NIL OR fontCodeInverse.length <= fontCode OR fontCodeInverse[fontCode] = NIL THEN ERROR InvalidFontCode[]; RETURN [fontCodeInverse[fontCode].fontObject] }; InvalidFontCode: PUBLIC ERROR ~ CODE; EnumerateFontCodes: PUBLIC ENTRY PROC [action: PROC [fontCode: FontCode, fontObject: FontObject] RETURNS [flush: BOOLEAN]] ~ { ENABLE UNWIND => NULL; FOR i: NAT IN [0..fontCodeTableHeaders) DO c: CollisionList _ fontCodeTable[i]; prev: CollisionList _ NIL; WHILE c#NIL DO cNext: CollisionList _ c.next; IF action[c.fontCode, c.fontObject] THEN { fontCodeInverse[c.fontCode] _ NIL; IF prev = NIL THEN fontCodeTable[i] _ cNext ELSE prev.next _ cNext; } ELSE {prev _ c}; c _ cNext; ENDLOOP; ENDLOOP; }; GetCharData: PUBLIC PROC [fontCache: FontCache, fontCode: FontCode, charCode: CARDINAL] RETURNS [charData: REF] ~ { GetCachedCharData: ENTRY PROC ~ INLINE { a: REF Array; IF fontCache.seq # NIL AND fontCode >= fontCache.firstFontCode AND fontCode-fontCache.firstFontCode < fontCache.seq.length AND (a _ fontCache.seq[fontCode-fontCache.firstFontCode]) # NIL AND charCode IN [a.bc..a.bc+a.length) THEN charData _ a[charCode-a.bc]; }; GetCachedCharData[]; IF charData = NIL THEN { fontObject: FontObject _ InterpretFontCode[fontCode]; charData _ fontObject.CharDataProc[fontObject, charCode].charData; LoadCharData[fontCache, fontCode, charCode, charData]; }; }; dummyArray: REF Array _ NEW[Array[0]]; stringCharsMapped, stringPiecesMapped: INT _ 0; GetStringData: PUBLIC PROC [action: PROC [charCode: CARDINAL, charData: REF], fontCache: FontCache, fontCode: FontCode, ropeOrRefText: REF, start: INT _ 0, length: INT _ LAST[INT]] ~ TRUSTED { rope: Rope.ROPE _ IF ISTYPE[ropeOrRefText, REF TEXT] THEN LOOPHOLE[ropeOrRefText] ELSE NARROW[ropeOrRefText]; reader: RopeReader.Ref _ RopeReader.GetRopeReader[]; GetCachedChars: ENTRY PROC ~ CHECKED INLINE { ENABLE UNWIND => NULL; a: REF Array; IF fontCache.seq # NIL AND fontCode >= fontCache.firstFontCode AND fontCode-fontCache.firstFontCode < fontCache.seq.length AND (a _ fontCache.seq[fontCode-fontCache.firstFontCode]) # NIL THEN { bc: CARDINAL _ a.bc; ecPlusOne: CARDINAL _ a.bc+a.length; WHILE length > 0 DO charCode: CARDINAL _ reader.Get-'\000; charData: REF _ NIL; IF charCode IN [bc..ecPlusOne) THEN charData _ a[charCode-bc]; IF charData = NIL THEN {reader.BackupIndex[1]; EXIT}; action[charCode, charData]; length _ length - 1; ENDLOOP; }; }; length _ MAX[MIN[length, rope.Length-start], 0]; reader.SetPosition[rope, start]; WHILE length > 0 DO GetCachedChars[]; IF length > 0 THEN { -- must have stopped because of a hard case charCode: CARDINAL _ reader.Get-'\000; charData: REF _ GetCharData[fontCache, fontCode, charCode]; action[charCode, charData]; length _ length - 1; }; ENDLOOP; reader.FreeRopeReader; }; LoadCharData: ENTRY PROC [fontCache: FontCache, fontCode: FontCode, charCode: CARDINAL, data: REF] ~ { ENABLE UNWIND => NULL; a: REF Array; IF fontCache.seq = NIL THEN {fontCache.seq _ NEW[Seq[6]]; fontCache.firstFontCode _ fontCode}; IF fontCode < fontCache.firstFontCode THEN { additionalLength: NAT ~ fontCache.firstFontCode - fontCode; newLength: NAT ~ additionalLength + fontCache.seq.length; old: REF Seq ~ fontCache.seq; fontCache.seq _ NEW[Seq[newLength]]; FOR i: NAT IN [0..old.length) DO fontCache.seq[additionalLength + i] _ old[i] ENDLOOP; fontCache.firstFontCode _ fontCode; }; IF fontCode-fontCache.firstFontCode >= fontCache.seq.length THEN { newLength: NAT ~ fontCode - fontCache.firstFontCode + 1 + fontCache.seq.length/3; old: REF Seq ~ fontCache.seq; fontCache.seq _ NEW[Seq[newLength]]; FOR i: NAT IN [0..old.length) DO fontCache.seq[i] _ old[i] ENDLOOP; }; a _ fontCache.seq[fontCode-fontCache.firstFontCode]; IF a = NIL THEN { bc: CARDINAL ~ MIN[charCode, ' -'\000]; ec: CARDINAL ~ MAX[charCode, '~-'\000]; a _ NEW[Array[ec-bc+1]]; a.bc _ bc; }; IF charCode < a.bc THEN { additionalLength: NAT ~ a.bc - charCode; newLength: NAT ~ additionalLength + a.length; old: REF Array _ a; a _ NEW[Array[newLength]]; FOR i: NAT IN [0..old.length) DO a[additionalLength + i] _ old[i] ENDLOOP; a.bc _ charCode; }; IF charCode >= a.bc+a.length THEN { newLength: NAT ~ charCode-a.bc + 1; old: REF Array _ a; a _ NEW[Array[newLength]]; FOR i: NAT IN [0..old.length) DO a[i] _ old[i] ENDLOOP; a.bc _ old.bc; }; a[charCode-a.bc] _ data; fontCache.seq[fontCode-fontCache.firstFontCode] _ a; }; END. μImagerFontCacheImpl.mesa Michael Plass, September 28, 1983 10:46 am Michael Plass, July 18, 1983 10:20 am: Fixed bug in GetFontCode; was checking fontCodeInverse.length against fontCode.key instead of nextFontCode. Michael Plass, July 22, 1983 2:07 pm: Put monitor in GetCharData and LoadCharData so a global font cache could be used. Michael Plass, September 28, 1983 9:27 am: Added GetStringData, GetNSStringData; made charCode a CARDINAL; replaced cacheMiss signal with FontObject. Κ ˜J™J™*IunitšΟk œ#˜,Kš œœœœœ˜[Jšœ œœœ˜*šœœœœ˜$Jšœœ˜Jšœœ˜ Jšœ˜—šœœœ˜Jšœ œœœ˜!Jšœ˜—šœœœ˜Jšœœ˜ Jšœœ œœ˜ Jšœ˜—Jšœ œœ˜Jšœ œ˜.Jšœœ!˜4Kšœœ˜ Kšœœœ˜Jšœ œœœ˜5Jšœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜—Jšœ œœ ˜0Jšœ ˜ šœ ˜Jšœ˜šœ œŸ+˜@Jšœ œ˜&Jšœ œ.˜;Jšœ˜Jšœ˜Jšœ˜—Jšœ˜—Jšœ˜Jšœ˜—š ž œœœ6œœ˜fJšœœœ˜Jšœœ˜ Jšœœœœ.˜^šœ$œ˜,Jšœœ&˜;Jšœ œ+˜9Jšœœ˜Jšœœ˜$Jš œœœœ.œ˜VJšœ#˜#Jšœ˜—šœ:œ˜BJšœ œC˜QJšœœ˜Jšœœ˜$Jš œœœœœ˜CJšœ˜—Jšœ4˜4šœœœ˜Jšœœœ˜'Jšœœœ˜'Jšœœ˜Jšœ ˜ Jšœ˜—šœœ˜Jšœœ˜(Jšœ œ˜-Jšœœ ˜Jšœœ˜Jš œœœœ"œ˜JJšœ˜Jšœ˜—šœœ˜#Jšœ œ˜#Jšœœ ˜Jšœœ˜Jš œœœœœ˜7Jšœ˜Jšœ˜—Jšœ˜Jšœ4˜4J˜—Kšœ˜Jšœ’™’Jšœw™wJšœaœ,™•—…—N%Ι