ImagerFontCacheImpl.mesa
Michael Plass, September 28, 1983 10:46 am
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 CARDINALLOOPHOLE[@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: NATIF 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: INTLAST[INT]] ~ TRUSTED {
rope: Rope.ROPEIF 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: REFNIL;
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.
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.