-- NodeAddrsImpl.mesa
-- written by Bill Paxton, March 1981
-- last edit by Bill Paxton, December 28, 1982 11:44 am
DIRECTORY
NodeProps,
TextNode,
NodeAddrs;
NodeAddrsImpl:
CEDAR MONITOR
IMPORTS NodeProps, TextNode
EXPORTS NodeAddrs =
BEGIN OPEN NodeAddrs;
TextAddrNotFound: PUBLIC ERROR = CODE;
addrsProp: ATOM = $TiogaNodeAddrsProp;
AddrsProp:
PUBLIC
PROC
RETURNS [
ATOM] = {
RETURN [addrsProp] };
Create: PROC RETURNS [RefAddrs] = INLINE { RETURN [TextNode.pZone.NEW[Body]] };
FindPair:
PROC [ref: RefAddrs, 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: RefTextNode]
RETURNS [ref: RefAddrs] =
INLINE {
RETURN [NARROW[NodeProps.GetProp[n, addrsProp]]] };
Put:
PROC [n: RefTextNode, ref: RefAddrs] =
INLINE {
NodeProps.PutProp[n, addrsProp, ref] };
PinTextAddr:
PUBLIC
PROC [n: RefTextNode, addr:
REF] = {
-- don't modify location when edits take place
ref: RefAddrs ← Get[n];
p: Pair;
IF (p ← FindPair[ref, addr])=NIL THEN RETURN;
p.pinned ← TRUE };
UnpinTextAddr:
PUBLIC
PROC [n: RefTextNode, addr:
REF] = {
ref: RefAddrs ← Get[n];
p: Pair;
IF (p ← FindPair[ref, addr])=NIL THEN RETURN;
p.pinned ← FALSE };
UnpinAll:
PUBLIC
PROC [n: RefTextNode] = {
ref: RefAddrs ← 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: RefTextNode, addr:
REF, location: Offset] = {
ref: RefAddrs ← 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: RefTextNode, addr:
REF, location: Offset] = {
-- assigns addr to location in node
-- ok if addr was previously assigned elsewhere in the node
ref: RefAddrs;
p: Pair;
IF (ref ← Get[n]) = NIL THEN Put[n, ref ← Create[]];
IF (p ← FindPair[ref, addr])=
NIL
THEN
ref.addrs ← p ← TextNode.pZone.NEW[PairBody ← [ref.addrs, FALSE, addr, NIL, location]]
ELSE p.location ← location;
p.pinned ← FALSE;
p.movedTo ← NIL };
RemTextAddr:
PUBLIC
PROC [n: RefTextNode, addr:
REF] = {
-- removes the given addr
ref: RefAddrs ← 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 NodeProps.PutProp[n, addrsProp, NIL] --remove-- };
GetTextAddr:
PUBLIC
PROC [n: RefTextNode, addr:
REF]
RETURNS [node: RefTextNode, location: Offset] = {
-- generates ERROR TextAddrNotFound if the addr is not in the mapping
ref: RefAddrs ← 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: RefTextNode, addr:
REF]
RETURNS [found: BOOLEAN, node: RefTextNode, location: Offset] = {
ref: RefAddrs ← 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: RefTextNode, action: TextAddrsAction]
RETURNS [BOOLEAN] = {
-- 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: RefAddrs ← 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 }};
AddNotifyProc:
PUBLIC
ENTRY
PROC [proc: AddrNotifyProc] = {
ENABLE UNWIND => NULL;
notify ← CONS[proc, notify];
};
RemoveNotifyProc:
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;
-- **** Editing Operations for persistent addrs ****
Replace:
PUBLIC
PROC [node: RefTextNode, start, len, newlen: Offset] = {
-- 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: Offset]
RETURNS [Offset] = {
RETURN [AfterReplace[old, start, len, newlen]] };
end: Offset ← start+len;
ref: RefAddrs = 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: Offset]
RETURNS [newLoc: Offset] = {
newLoc ←
SELECT initLoc
FROM
>= start+len => initLoc-len+newlen,
>= start => start,
ENDCASE => initLoc};
-- ***** Initialization
StartNodeAddrs:
PUBLIC
PROC = {
OPEN NodeProps;
Register[addrsProp,NullRead,NullWrite,NullCopy] };
END.