NodeAddrsImpl.mesa
Copyright © 1985, 1986 by Xerox Corporation. All rights reserved.
written by Bill Paxton, March 1981
last edit by Bill Paxton, December 28, 1982 11:44 am
Michael Plass, March 29, 1985 2:34:11 pm PST
Doug Wyatt, September 17, 1986 10:45:06 am PDT
DIRECTORY
Tioga,
TiogaPrivate;
NodeAddrsImpl: CEDAR MONITOR
IMPORTS Tioga, TiogaPrivate
EXPORTS TiogaPrivate
= BEGIN OPEN Tioga, TiogaPrivate;
Addrs: TYPE = REF AddrsBody;
AddrsBody: TYPE = RECORD [addrs: Pair];
Pair: TYPE = REF PairBody;
PairBody: TYPE = RECORD [
next: Pair,
pinned: BOOLFALSE, -- if true, then don't modify location when edits take place
addr: REF,
movedTo: Node, -- NIL if hasn't been moved
location: INT
];
TextAddrNotFound: PUBLIC ERROR = CODE;
addrsProp: ATOM = $TiogaNodeAddrsProp;
AddrsProp: PUBLIC PROC RETURNS [ATOM] = {
RETURN [addrsProp];
};
FindPair: PROC [ref: Addrs, addr: REF] RETURNS [Pair] = {
IF ref#NIL THEN
FOR p: Pair ← ref.addrs, p.next UNTIL p=NIL DO
IF p.addr = addr THEN RETURN [p]; ENDLOOP;
RETURN [NIL];
};
Get: PROC [n: Node] RETURNS [ref: Addrs] = INLINE {
RETURN [NARROW[GetProp[n, addrsProp]]];
};
Put: PROC [n: Node, ref: Addrs] = INLINE {
PutProp[n, addrsProp, ref];
};
PinTextAddr: PUBLIC PROC [n: Node, addr: REF] = {
don't modify location when edits take place
ref: Addrs ← Get[n];
p: Pair;
IF (p ← FindPair[ref, addr])=NIL THEN RETURN;
p.pinned ← TRUE;
};
UnpinTextAddr: PUBLIC PROC [n: Node, addr: REF] = {
ref: Addrs ← Get[n];
p: Pair;
IF (p ← FindPair[ref, addr])=NIL THEN RETURN;
p.pinned ← FALSE;
};
UnpinAllAddrs: PUBLIC PROC [n: Node] = {
ref: Addrs ← Get[n];
IF ref#NIL THEN
FOR p: Pair ← ref.addrs, p.next UNTIL p=NIL DO
p.pinned ← FALSE;
ENDLOOP;
};
MoveTextAddr: PUBLIC PROC [from, to: Node, addr: REF, location: INT] = {
ref: Addrs ← Get[from];
p: Pair;
IF (p ← FindPair[ref, addr])=NIL OR to=NIL THEN RETURN;
p.movedTo ← to;
PutTextAddr[to, addr, location];
};
PutTextAddr: PUBLIC PROC [n: Node, addr: REF, location: INT, pin: BOOLFALSE] = {
assigns addr to location in node
ok if addr was previously assigned elsewhere in the node
ref: Addrs;
p: Pair;
IF (ref ← Get[n]) = NIL THEN Put[n, ref ← NEW[AddrsBody]];
IF (p ← FindPair[ref, addr])=NIL THEN
ref.addrs ← p ← NEW[PairBody ← [ref.addrs, FALSE, addr, NIL, location]]
ELSE p.location ← location;
p.pinned ← pin;
p.movedTo ← NIL;
};
RemTextAddr: PUBLIC PROC [n: Node, addr: REF] = {
removes the given addr
ref: Addrs ← Get[n];
p, prev: Pair;
IF ref=NIL OR (p ← ref.addrs)=NIL THEN RETURN;
IF p.addr=addr THEN ref.addrs ← p.next
ELSE DO
prev ← p; p ← p.next;
IF p=NIL THEN EXIT;
IF p.addr=addr THEN { prev.next ← p.next; EXIT };
ENDLOOP;
IF ref.addrs=NIL THEN PutProp[n, addrsProp, NIL]; -- remove
};
GetTextAddr: PUBLIC PROC [n: Node, addr: REF]
RETURNS [node: Node, location: INT] = {
generates ERROR TextAddrNotFound if the addr is not in the mapping
ref: Addrs ← Get[n];
p: Pair;
IF (p ← FindPair[ref, addr])=NIL THEN ERROR TextAddrNotFound;
IF p.movedTo # NIL THEN [node, location] ← GetTextAddr[p.movedTo, addr]
ELSE RETURN [n, p.location];
};
TryGetTextAddr: PUBLIC PROC [n: Node, addr: REF]
RETURNS [found: BOOL, node: Node, location: INT] = {
ref: Addrs ← Get[n];
p: Pair;
IF (p ← FindPair[ref, addr])=NIL THEN RETURN [FALSE, n, 0];
IF p.movedTo # NIL THEN [found, node, location] ← TryGetTextAddr[p.movedTo, addr]
ELSE RETURN [TRUE, n, p.location];
};
MapTextAddrs: PUBLIC PROC [n: Node, action: TextAddrsAction]
RETURNS [BOOL] = {
apply the action to each addr&location pair for the node
returns true if&when an action returns true
skips pinned or moved addresses
ref: Addrs ← Get[n];
IF ref#NIL THEN {
p: Pair ← ref.addrs;
UNTIL p=NIL DO
next: Pair ← p.next;
IF p.pinned OR p.movedTo # NIL THEN NULL
ELSE IF action[p.addr, p.location] THEN RETURN [TRUE];
p ← next;
ENDLOOP;
};
RETURN [FALSE];
};
**** notify proc registration ****
notify: LIST OF AddrNotifyProc;
unlocked: CONDITION;
lockCount: INTEGER ← 0;
Lock: ENTRY PROC = { lockCount ← lockCount+1 };
Unlock: ENTRY PROC = {
IF (lockCount ← lockCount-1) <= 0 THEN {
lockCount ← 0; BROADCAST unlocked;
};
};
AddAddrNotifyProc: PUBLIC ENTRY PROC [proc: AddrNotifyProc] = {
ENABLE UNWIND => NULL;
notify ← CONS[proc, notify];
};
RemoveAddrNotifyProc: PUBLIC ENTRY PROC [proc: AddrNotifyProc] = {
ENABLE UNWIND => NULL;
prev: LIST OF AddrNotifyProc;
IF notify = NIL THEN RETURN;
IF notify.first = proc THEN { notify ← notify.rest; RETURN };
WHILE lockCount > 0 DO WAIT unlocked; ENDLOOP;
prev ← notify;
FOR l: LIST OF AddrNotifyProc ← notify.rest, l.rest UNTIL l = NIL DO
IF l.first = proc THEN { prev.rest ← l.rest; RETURN };
prev ← l;
ENDLOOP;
};
Notify: AddrNotifyProc = {
ENABLE UNWIND => Unlock[];
Lock[];
FOR l: LIST OF AddrNotifyProc ← notify, l.rest UNTIL l = NIL DO
l.first[node, new]; ENDLOOP;
Unlock[];
};
**** Editing Operations for persistent addrs ****
AddrReplace: PUBLIC PROC [node: Node, start, len, newlen: INT] = {
replace chars in [start..start+len) by newlen chars
addrs that are in the replaced section move to start
add (newlen-len) to locations that are after the replaced section
replace: PROC [old: INT] RETURNS [INT] = {
RETURN [AfterReplace[old, start, len, newlen]] };
end: INT ← start+len;
ref: Addrs = Get[node];
Notify[node, replace];
IF ref=NIL THEN RETURN;
FOR p: Pair ← ref.addrs, p.next UNTIL p=NIL DO
IF p.pinned OR p.movedTo # NIL THEN LOOP;
SELECT p.location FROM
>= end => p.location ← p.location-len+newlen;
>= start => p.location ← start;
ENDCASE;
ENDLOOP;
};
AfterReplace: PUBLIC PROC [initLoc, start, len, newlen: INT]
RETURNS [newLoc: INT] = {
newLoc ← SELECT initLoc FROM
>= start+len => initLoc-len+newlen,
>= start => start,
ENDCASE => initLoc;
};
***** Initialization
RegisterProp[addrsProp, NullRead, NullWrite, NullCopy];
END.