<> <> <> <> <> <> DIRECTORY NodeAddrs, NodeProps; NodeAddrsImpl: CEDAR MONITOR IMPORTS NodeProps EXPORTS NodeAddrs = BEGIN OPEN NodeAddrs; TextAddrNotFound: PUBLIC ERROR = CODE; addrsProp: ATOM = $TiogaNodeAddrsProp; AddrsProp: PUBLIC PROC RETURNS [ATOM] = { RETURN [addrsProp] }; 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] = { <> 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] = { <> <> ref: RefAddrs; p: Pair; IF (ref _ Get[n]) = NIL THEN Put[n, ref _ NEW[Body]]; IF (p _ FindPair[ref, addr])=NIL THEN ref.addrs _ p _ 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] = { <> 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] = { <> 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: BOOL, 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 [BOOL] = { <> <> <> 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; Unlock[] }; <<**** Editing Operations for persistent addrs ****>> Replace: PUBLIC PROC [node: RefTextNode, start, len, newlen: Offset] = { <> <> <> 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: PROC = { OPEN NodeProps; Register[addrsProp,NullRead,NullWrite,NullCopy] }; StartNodeAddrs[]; END.