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 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.
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.