UFFileManagerImpl.mesa
Created January 7, 1983
Last edit by Michael Plass on February 25, 1983 10:47 am
DIRECTORY CIFS, Environment, File, Process, RefText, Rope, RTTypesBasic, SafeStorage, Space, UFFileManager;
UFFileManagerImpl: CEDAR MONITOR
IMPORTS CIFS, File, Process, RefText, Rope, RTTypesBasic, SafeStorage, Space
EXPORTS UFFileManager =
BEGIN OPEN UFFileManager;
FontFile: PUBLIC TYPE = REF FontFilePermission;
FontFilePermission:
PUBLIC
TYPE =
RECORD [
link: REF FontFilePermission,
key: Key
];
qZone:
ZONE ← SafeStorage.NewZone[quantized];
Used only for FontFilePermissions.
FontFileDescription:
TYPE =
RECORD [
permissions: REF FontFilePermission,
permissionHint: REF FontFilePermission,
clientData: REF ← NIL,
openFile: CIFS.OpenFile,
errorCode: CIFS.ErrorCode,
errorMsg: Rope.ROPE,
space: Space.Handle,
origin: LONG POINTER,
size: INT -- in words
];
keyTable: REF KeyTableRec ← NEW[KeyTableRec[6]];
KeyTableRec:
TYPE =
RECORD [
numberOfEntries: NAT ← 0,
entries: SEQUENCE length: NAT OF NameFilePair
];
NameFilePair: TYPE = RECORD [fileName: ROPE, description: REF FontFileDescription];
permissionCount: INT ← 0;
permissionsSinceCollectionRequest: NAT ← 0;
permissionThreshold: NAT ← 96;
RequestCollection:
PROC = {
permissionsSinceCollectionRequest ← 0;
SafeStorage.ReclaimCollectibleObjects[];
};
finalizationQueue: RTTypesBasic.FinalizationQueue ← RTTypesBasic.NewFQ[permissionThreshold];
FinalizerProcess:
PROCEDURE [finalizationQueue: RTTypesBasic.FinalizationQueue] = {
DO
permission: REF FontFilePermission ← NARROW[RTTypesBasic.FQNext[finalizationQueue]];
key: Key ← permission.key;
closed: BOOLEAN ← Finalize[permission];
permission ← NIL;
IF closed THEN fontFileWatcherProc[key, close];
IF RTTypesBasic.FQEmpty[finalizationQueue] THEN RequestCollection[];
ENDLOOP;
};
Finalize:
ENTRY
PROCEDURE [permission:
REF FontFilePermission]
RETURNS [closed:
BOOLEAN ←
FALSE] =
TRUSTED {
ENABLE UNWIND => NULL;
key: Key ← permission.key;
description: REF FontFileDescription ← keyTable[key.index].description;
IF permission = description.permissions
THEN {
description.permissions ← permission.link;
}
ELSE {
prev: REF FontFilePermission ← description.permissions;
FOR p:
REF FontFilePermission ← description.permissions.link, p.link
UNTIL p=permission
DO
prev ← p;
ENDLOOP;
prev.link ← permission.link;
};
permission.link ← NIL;
IF description.permissions =
NIL
THEN {
IF description.openFile #
NIL
THEN {
Space.Delete[description.space];
CIFS.Close[description.openFile];
closed ← TRUE;
};
keyTable[key.index].description ← NIL;
};
permissionCount ← permissionCount-1;
};
Init:
ENTRY
PROCEDURE =
TRUSTED {
ENABLE UNWIND => NULL;
RTTypesBasic.EstablishFinalization[CODE[FontFilePermission], 1, finalizationQueue];
Process.Detach[FORK FinalizerProcess[finalizationQueue]];
};
KeyOf:
PUBLIC
ENTRY
PROCEDURE [fileName:
ROPE]
RETURNS [key: Key] = {
ENABLE UNWIND => NULL;
len: NAT ← fileName.Length[];
canonicalName: REF TEXT ← RefText.ObtainScratch[len];
i: NAT ← 0;
Getc: PROC RETURNS [ch: CHAR] = {IF i<len THEN {ch𡤏ileName.Fetch[i]; i←i+1} ELSE ch←' };
c: CHAR ← Getc[];
j: NAT ← 0;
Put: PROC [ch: CHAR] = {canonicalName[j] ← ch; j ← j + 1};
PreventTableOverflow:
PROC = {
entries: NAT ← keyTable.numberOfEntries;
IF entries = keyTable.length
THEN {
newKeyTable: REF KeyTableRec ← NEW[KeyTableRec[MIN[entries+entries/3+1, LAST[NAT]-1]]];
newKeyTable.numberOfEntries ← entries;
FOR l:
NAT
IN [0..entries)
DO
newKeyTable[l] ← keyTable[l];
ENDLOOP;
keyTable ← newKeyTable;
};
};
Match:
PROC [rope: Rope.
ROPE]
RETURNS [
BOOLEAN] = {
IF rope.Length # j THEN RETURN[FALSE];
FOR i:
NAT
DECREASING
IN [0..j)
DO
IF rope.Fetch[i] # canonicalName[i] THEN RETURN[FALSE];
ENDLOOP;
RETURN[TRUE]
};
lowerToUpper: INTEGER ← 'A - 'a;
key ← [index:LAST[NAT]];
WHILE c#'
DO
SELECT c
FROM
'[ => {Put['/]; c ← Getc[]};
'] => {Put['/]; IF (c ← Getc[]) = '< THEN c ← Getc[]};
'> => {Put['/]; c ← Getc[]};
IN ['a .. 'z] => {Put[c + lowerToUpper]; c ← Getc[]};
ENDCASE => {Put[c]; c ← Getc[]};
ENDLOOP;
canonicalName.length ← j;
FOR k:
NAT
IN [0..keyTable.numberOfEntries)
DO
IF Match[keyTable[k].fileName] THEN {key ← [index: k]; EXIT};
ENDLOOP;
IF key.index =
LAST[
NAT]
THEN {
PreventTableOverflow[];
keyTable[keyTable.numberOfEntries] ← [fileName: Rope.FromRefText[canonicalName], description: NIL];
key ← [index: keyTable.numberOfEntries];
keyTable.numberOfEntries ← keyTable.numberOfEntries+1;
};
RefText.ReleaseScratch[canonicalName];
};
KeyOfRefText:
PUBLIC
PROCEDURE [fileName:
REF
TEXT]
RETURNS [key: Key] = {
key ← KeyOf[RefText.TrustTextAsRope[fileName]];
};
waitTime: Process.Ticks ← Process.MsecToTicks[5000];
fileHintTimeout: CONDITION;
fileHintTimeoutProcess: PROCESS ← NIL;
FileHintTimeoutProcess:
ENTRY
PROCEDURE = {
ENABLE UNWIND => NULL;
WAIT fileHintTimeout;
fileHintTimeoutProcess ← NIL;
FOR i:
NAT
IN [0..keyTable.numberOfEntries)
DO
description: REF FontFileDescription ← keyTable[i].description;
IF description#NIL THEN description.permissionHint ← NIL;
ENDLOOP;
WAIT fileHintTimeout;
IF fileHintTimeoutProcess = NIL THEN SafeStorage.ReclaimCollectibleObjects[suspendMe: FALSE];
};
StartFileHintTimeout:
ENTRY
PROCEDURE =
TRUSTED {
ENABLE UNWIND => NULL;
IF fileHintTimeoutProcess=
NIL
THEN {
Process.InitializeCondition[@fileHintTimeout, waitTime];
Process.Detach[fileHintTimeoutProcess ← FORK FileHintTimeoutProcess[]];
};
};
Open:
PUBLIC
PROCEDURE [key: Key, initProc: InitProc ←
NIL]
RETURNS [fontFile: FontFile] = {
description: REF FontFileDescription ← keyTable[key.index].description;
IF description#NIL THEN fontFile ← description.permissionHint;
IF fontFile=
NIL
THEN {
triedToOpen: BOOLEAN;
[fontFile, triedToOpen] ← MonitoredOpen[key, initProc];
IF triedToOpen THEN fontFileWatcherProc[key, IF Status[fontFile].ok THEN open ELSE openFailed];
IF permissionsSinceCollectionRequest>permissionThreshold THEN RequestCollection[];
};
IF fileHintTimeoutProcess=NIL THEN StartFileHintTimeout[];
};
MonitoredOpen:
ENTRY
PROCEDURE [key: Key, initProc: InitProc ←
NIL]
RETURNS [fontFile: FontFile, triedToOpen:
BOOLEAN ←
FALSE] = {
ENABLE UNWIND => NULL;
description: REF FontFileDescription ← keyTable[key.index].description;
openOk: BOOLEAN ← TRUE;
IF description =
NIL
THEN {
description ← NEW[FontFileDescription];
description.space ← Space.nullHandle;
description.openFile ← CIFS.Open[keyTable[key.index].fileName, CIFS.read + CIFS.dontCheck ! CIFS.Error => TRUSTED {openOk ← FALSE; description.errorCode ← code; description.errorMsg ← error; CONTINUE}];
triedToOpen ← TRUE;
IF openOk
THEN
TRUSTED {
capability: File.Capability ← CIFS.GetFC[description.openFile];
pages: File.PageCount ← File.GetSize[capability];
description.size ← INT[pages]*Environment.wordsPerPage;
description.space ← Space.Create[size: pages, parent: Space.virtualMemory];
Space.Map[space: description.space, window: [file: capability, base: 1]];
description.origin ← Space.LongPointer[description.space];
}
ELSE description.openFile ← NIL;
};
keyTable[key.index].description ← description;
fontFile ← qZone.NEW[FontFilePermission];
permissionCount ← permissionCount + 1;
permissionsSinceCollectionRequest ← permissionsSinceCollectionRequest + 1;
fontFile.link ← description.permissions;
description.permissions ← fontFile;
fontFile.key ← key;
IF description.clientData =
NIL
AND description.openFile #
NIL
AND initProc #
NIL
THEN
description.clientData ← initProc[key, fontFile];
description.permissionHint ← fontFile;
};
GetData:
PUBLIC
PROCEDURE [fontFile: FontFile]
RETURNS [
REF] = {
RETURN[keyTable[fontFile.key.index].description.clientData];
};
Status:
PUBLIC
PROCEDURE [fontFile: FontFile]
RETURNS [errorCode:
CIFS.ErrorCode, errorMsg: Rope.
ROPE, ok:
BOOLEAN] = {
description: REF FontFileDescription ← keyTable[fontFile.key.index].description;
errorCode ← description.errorCode;
errorMsg ← description.errorMsg;
ok ← description.openFile # NIL
};
Pointer:
PUBLIC
UNSAFE
PROCEDURE [fontFile: FontFile]
RETURNS [origin:
LONG
POINTER] = {
origin ← keyTable[fontFile.key.index].description.origin;
};
NameOf:
PUBLIC
PROCEDURE [key: Key]
RETURNS [fileName:
ROPE] = {
IF key.index<keyTable.numberOfEntries THEN fileName ← keyTable[key.index].fileName;
};
Size:
PUBLIC
PROCEDURE [fontFile: FontFile]
RETURNS [sizeInSixteenBitWords:
INT] = {
sizeInSixteenBitWords ← keyTable[fontFile.key.index].description.size;
};
InBounds:
PUBLIC
UNSAFE
PROCEDURE [fontFile: FontFile, blockAddress:
LONG
POINTER, sizeInWords:
NAT]
RETURNS [
BOOLEAN] =
UNCHECKED {
description: REF FontFileDescription ← keyTable[fontFile.key.index].description;
addr: LONG CARDINAL ← LOOPHOLE[blockAddress];
org: LONG CARDINAL ← LOOPHOLE[description.origin];
end: LONG CARDINAL ← org + description.size;
RETURN[
sizeInWords >= 0 AND
addr >= org AND
addr + sizeInWords <= end
];
};
fontFileWatcherProc: FontFileWatcher ← defaultFontFileWatcher;
defaultFontFileWatcher: FontFileWatcher = {};
RegisterFontFileWatcher:
PUBLIC
PROCEDURE [fontFileWatcher: FontFileWatcher] = {
fontFileWatcherProc ← fontFileWatcher;
};
PermissionCount: PUBLIC PROCEDURE RETURNS [INT] = {RETURN[permissionCount]};
EnumerateOpenFontFiles:
PUBLIC
PROCEDURE [visit:
PROC[Rope.
ROPE]] = {
This is written so it doesn't have to be an entry procedure, so it doesn't matter if the visit proc hangs.
i: NAT ← 0;
DO
table: REF KeyTableRec ← keyTable;
rope: Rope.ROPE ← NIL;
IF i>= table.numberOfEntries THEN EXIT;
IF table[i].description # NIL THEN rope ← table[i].fileName;
table ← NIL;
IF rope # NIL THEN visit[rope];
i ← i+1;
ENDLOOP;
};
Init[];
END.