-- RefTabImpl.Mesa - implementation of REF table abstraction
-- last change: Bill Paxton - 8-Feb-82 10:06:02

DIRECTORY
   Inline USING [LowHalf],
   SafeStorage USING [GetSystemZone],
   RefTab;

RefTabImpl: MONITOR LOCKS x USING x: Ref
      IMPORTS Inline, SafeStorage
      EXPORTS RefTab
      = BEGIN

  Int: TYPE = LONG INTEGER;
  Ref: TYPE = REF RefTabRep;
  RefTabRep: PUBLIC TYPE =
    MONITORED RECORD [mod,size: CARDINAL, zone: ZONE, data: REF Array];
  NodeRep: TYPE = RECORD [key: Key, val: Val, next: Node];
  Node: TYPE = REF NodeRep;
  Key: TYPE = RefTab.Key; 
  Val: TYPE = RefTab.Val;
  EachPairAction: TYPE = RefTab.EachPairAction; 

  Array: TYPE = RECORD[nodes: SEQUENCE max: CARDINAL OF Node];

  -- Private Operations

  Hash: PROCEDURE [key: Key, mod: CARDINAL] RETURNS [CARDINAL] = INLINE BEGIN
     RETURN [(LOOPHOLE[Inline.LowHalf[key],CARDINAL]/4) MOD mod];
    END;

  -- public operations

  Create: PUBLIC PROCEDURE [mod: CARDINAL ← 17, zone: ZONE ← NIL]
          RETURNS [Ref]
    = BEGIN
    index: CARDINAL ← 0;
    data: REF Array ← NIL;
    IF mod < 1 THEN mod ← 1;
    IF mod > 4095 THEN mod ← 4095;
    IF zone=NIL THEN zone ← SafeStorage.GetSystemZone[];
    data ← zone.NEW[Array[mod]];
    RETURN[zone.NEW[RefTabRep ← [mod: mod, size: 0, zone: zone, data: data]]];
    END;

  GetSize: PUBLIC PROCEDURE [x: Ref] RETURNS [CARDINAL] =
    {RETURN [x↑.size]};

  Fetch: PUBLIC ENTRY PROCEDURE [x: Ref, key: Key]
         RETURNS [found: BOOLEAN, val: Val] = BEGIN
    ENABLE UNWIND => NULL;
    hash: CARDINAL ← Hash[key, x.mod];
    node: Node ← x.data[hash];
    WHILE node # NIL DO
      IF key = node.key THEN RETURN [TRUE, node.val];
      node ← node.next;
      ENDLOOP;
    RETURN [FALSE, NIL];
    END;

  Store: PUBLIC ENTRY PROCEDURE [x: Ref, key: Key, val: Val]
         RETURNS [BOOLEAN]
    = BEGIN
    ENABLE UNWIND => NULL;
    hash: CARDINAL ← Hash[key, x.mod];
    node: Node ← x.data[hash];
    head: Node ← node;
    WHILE node # NIL DO
      IF key = node.key THEN {node.val ← val; RETURN [FALSE]};
      node ← node.next;
      ENDLOOP;
    x.data[hash] ← x.zone.NEW[NodeRep ← [key: key, val: val, next: head]];
    x.size ← x.size + 1;
    RETURN [TRUE];
    END;

  Insert: PUBLIC ENTRY PROCEDURE [x: Ref, key: Key, val: Val]
          RETURNS [BOOLEAN]
    = BEGIN
    ENABLE UNWIND => NULL;
    hash: CARDINAL ← Hash[key, x.mod];
    node: Node ← x.data[hash];
    head: Node ← node;
    WHILE node # NIL DO
      IF key = node.key THEN RETURN [FALSE];
      node ← node.next;
      ENDLOOP;
    x.data[hash] ← x.zone.NEW[NodeRep ← [key: key, val: val, next: head]];
    x.size ← x.size + 1;
    RETURN [TRUE];
    END;

  Delete: PUBLIC ENTRY PROCEDURE [x: Ref, key: Key] RETURNS [BOOLEAN]
    = BEGIN
    ENABLE UNWIND => NULL;
    hash: CARDINAL ← Hash[key, x.mod];
    node: Node ← x.data[hash];
    head: Node ← node;
    lag: Node ← NIL;
    WHILE node # NIL DO
      IF key = node.key THEN {
      	IF lag = NIL THEN x.data[hash] ← node.next
        ELSE lag.next ← node.next;
        x.size ← x.size - 1;
        RETURN [TRUE]};
      lag ← node;
      node ← node.next;
      ENDLOOP;
    RETURN [FALSE];
    END;

  Pairs: PUBLIC PROCEDURE [x: Ref, action: EachPairAction]
           RETURNS [quit: BOOLEAN] = BEGIN
    node: Node ← NIL;
    index: CARDINAL ← 0;
    DO
        [node, index] ← GetNext[x, node, index];
        IF node = NIL THEN RETURN [FALSE];
        IF action[node.key, node.val] THEN RETURN [TRUE];
        ENDLOOP;
    END;

  GetNext: ENTRY PROCEDURE [x: Ref, node: Node, index: CARDINAL]
             RETURNS [Node, CARDINAL] = {
    ENABLE UNWIND => NULL;
    mod: CARDINAL ← x.mod;
    IF node # NIL THEN {
       node ← node.next;
       IF node # NIL THEN RETURN [node, index];
       index ← index + 1}
       ELSE index ← 0;
    WHILE index < mod DO
      node ← x.data[index];
      IF node = NIL THEN {index ← index + 1; LOOP};
      RETURN [node, index];
      ENDLOOP;
    RETURN [NIL, mod];
    };

  END...