<> <> DIRECTORY XFontCache; XFontCacheImpl: CEDAR MONITOR EXPORTS XFontCache = 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: CHAR, seq: SEQUENCE length: [0..256) OF REF ]; FontCode: TYPE ~ XFontCache.FontCode; fontCodeTableHeaders: NAT ~ 101; FontKeyRec: TYPE ~ MACHINE DEPENDENT RECORD [a, b, c, d: REAL, font: REF]; fontCodeTable: REF FontCodeTableRep _ NEW[FontCodeTableRep]; FontCodeTableRep: TYPE ~ ARRAY [0..fontCodeTableHeaders) OF CollisionList; CollisionList: TYPE ~ REF CollisionListItem; CollisionListItem: TYPE ~ RECORD [ next: CollisionList, fontCode: FontCode, fontKeyRec: FontKeyRec ]; fontCodeInverse: REF FontCodeInverseRep; FontCodeInverseRep: TYPE ~ RECORD [SEQUENCE length: NAT OF CollisionList]; nextFontCode: CARDINAL _ 0; GetFontCode: PUBLIC ENTRY PROC [a, b, c, d: REAL, font: REF] RETURNS [fontCode: FontCode] ~ TRUSTED { ENABLE UNWIND => NULL; fontKeyRec: FontKeyRec _ [a, b, c, d, font]; p: POINTER TO CARDINAL _ LOOPHOLE[@fontKeyRec]; hash: CARDINAL _ 0; IF SIZE[FontKeyRec] MOD SIZE[CARDINAL] # 0 THEN ERROR; THROUGH [0..SIZE[FontKeyRec]/SIZE[CARDINAL]) DO t: CARDINAL _ 2*hash; hash _ 2*t+hash+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.fontKeyRec = fontKeyRec THEN RETURN [c.fontCode]; ENDLOOP; fontCodeTable[hash] _ NEW[CollisionListItem _ [fontCodeTable[hash], [nextFontCode], fontKeyRec]]; IF fontCodeInverse = NIL OR fontCodeInverse.length <= fontCode.key 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 [a, b, c, d: REAL, font: REF] ~ { ENABLE UNWIND => NULL; IF fontCodeInverse = NIL OR fontCodeInverse.length <= fontCode.key OR fontCodeInverse[fontCode.key] = NIL THEN ERROR InvalidFontCode[]; [a, b, c, d, font] _ fontCodeInverse[fontCode.key].fontKeyRec; }; InvalidFontCode: PUBLIC ERROR ~ CODE; Flush: PUBLIC ENTRY PROC [font: REF] ~ { ENABLE UNWIND => NULL; FOR i: NAT IN [0..fontCodeTableHeaders) DO c: CollisionList _ fontCodeTable[i]; prev: CollisionList _ NIL; WHILE c#NIL DO IF c.fontKeyRec.font = font THEN { fontCodeInverse[c.fontCode.key] _ NIL; IF prev = NIL THEN fontCodeTable[i] _ c.next ELSE prev.next _ c.next; } ELSE prev _ c; c _ IF prev = NIL THEN fontCodeTable[i] ELSE prev.next; ENDLOOP; ENDLOOP; }; Create: PUBLIC PROC RETURNS [FontCache] ~ { RETURN [NEW[FontCacheRep]]; }; GetCharData: PUBLIC PROC [fontCache: FontCache, fontCode: FontCode, char: CHAR] RETURNS [data: REF] ~ { code: NAT ~ fontCode.key; IF fontCache.seq = NIL OR code < fontCache.firstFontCode OR code-fontCache.firstFontCode >= fontCache.seq.length THEN GOTO Miss ELSE { a: REF Array _ fontCache.seq[code-fontCache.firstFontCode]; IF a = NIL OR char < a.bc OR char-a.bc >= a.length THEN GOTO Miss; data _ a[char-a.bc]; IF data = NIL THEN GOTO Miss; }; EXITS Miss => { data _ SIGNAL CacheMiss[fontCode, char]; LoadCharData[fontCache, fontCode, char, data]; }; }; CacheMiss: PUBLIC SIGNAL [fontCode: FontCode, char: CHAR] RETURNS [data: REF] = CODE; LoadCharData: PUBLIC PROC [fontCache: FontCache, fontCode: FontCode, char: CHAR, data: REF] ~ { code: NAT ~ fontCode.key; a: REF Array; IF fontCache.seq = NIL THEN {fontCache.seq _ NEW[Seq[6]]; fontCache.firstFontCode _ code}; IF code < fontCache.firstFontCode THEN { additionalLength: NAT ~ fontCache.firstFontCode - code; 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 _ code; }; IF code-fontCache.firstFontCode >= fontCache.seq.length THEN { newLength: NAT ~ code - 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[code-fontCache.firstFontCode]; IF fontCache.seq[code-fontCache.firstFontCode] = NIL THEN { bc: CHAR ~ MIN[char, ' ]; ec: CHAR ~ MAX[char, '~]; a _ NEW[Array[ec-bc+1]]; a.bc _ bc; }; IF char < a.bc THEN { additionalLength: NAT ~ a.bc - char; 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 _ char; }; IF char-a.bc >= a.length THEN { newLength: NAT ~ char-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[char-a.bc] _ data; fontCache.seq[code-fontCache.firstFontCode] _ a; }; END.