GENERIC MODULEThe node pointed to bySkipListTbl (Key, Value, Tbl, SortedTbl); IMPORT Random, Word; TYPE ForwardPtrs = REF ARRAY OF Node; Node = REF RECORD k: Key.T; v: Value.T; back: Node := NIL; forward: ForwardPtrs := NIL; END; REVEAL T = Public BRANDED Brand OBJECT num: CARDINAL; (* number of elements in the table *) maxLevel: CARDINAL; (* READONLY after "init" *) level: CARDINAL; (* current list level *) root: Node := NIL; (* root node; its "k" and "v" are ignored *) update: ForwardPtrs; (* scratch area used in "put", "delete" *) rand: Random.T := NIL; (* random number generator *) randBits: INTEGER; (* cache of random bits *) bitsRem: CARDINAL; (* number of random bits remaining in "lastRand" *) OVERRIDES keyCompare := KeyCompare; init := Init; size := Size; get := Get; put := Put; delete := Delete; iterate := Iterate; iterateOrdered := IterateOrdered; END;
root
stores level
forward pointers, and
its back
field points to the last element of the list if the list
is non-empty.
Invariants that hold after init
:
I0. root # NIL I1. (root.back = NIL) = (num = 0) I1. level <= maxLevel I2. NUMBER(update^) = NUMBER(root.forward^) = maxLevel I3. (forall i in [level, maxLevel): root.forward[i] = NIL) I4. rand # NIL AND bitsRem = Word.SizeCONST IterBrand = "(Iterator " & Brand & ")"; REVEAL Iterator = IteratorPublic BRANDED IterBrand OBJECT t: T; (* corresponding tree *) curr: Node; (* current node in iteration *) END; TYPE IteratorUp = Iterator OBJECT OVERRIDES reset := ResetUp; next := NextUp; seek := SeekUp; END; IteratorDown = Iterator OBJECT OVERRIDES reset := ResetDown; next := NextDown; seek := SeekDown; END;Utility procedures ------------------------------------------------------PROCEDUREFlipOneFourthCoin (t: T): INTEGER = VAR res := Word.And(t.randBits, 2_11); BEGIN IF t.bitsRem > 2 THEN t.randBits := Word.RightShift(t.randBits, 2); DEC(t.bitsRem, 2) ELSE t.randBits := t.rand.integer(); t.bitsRem := Word.Size END; RETURN res; END FlipOneFourthCoin; PROCEDURERandLevel (t: T): CARDINAL = VAR level: CARDINAL := 1; BEGIN WHILE level < t.maxLevel AND FlipOneFourthCoin(t) = 0 DO INC(level); END; RETURN level END RandLevel; PROCEDURENewNode (l: CARDINAL; READONLY k: Key.T; READONLY v: Value.T): Node =Return a new node with levell
, keyk
, and valuev
.BEGIN RETURN NEW(Node, k := k, v := v, forward := NEW(ForwardPtrs, l)) END NewNode; PROCEDURESeek (t: T; READONLY k: Key.T): Node =Return the highest node int
with key less thankey
.VAR alreadyChecked: Node := NIL; n := t.root; BEGIN FOR i := t.level - 1 TO 0 BY -1 DO VAR next := n.forward[i]; BEGIN WHILE next # alreadyChecked AND next # NIL AND t.keyCompare(next.k, k) < 0 DO n := next; next := n.forward[i] END; alreadyChecked := next; END END; RETURN n END Seek;Tree method implementations ---------------------------------------------PROCEDUREIterator method implementations ----------------------------------------KeyCompare (<*UNUSED*> t: T; READONLY k1, k2: Key.T): [-1..1] = BEGIN RETURN Key.Compare(k1, k2) END KeyCompare; PROCEDUREInit (t: T; maxSizeHint: CARDINAL; fixedSeed: BOOLEAN): T = BEGIN (* compute "t.maxLevel" as "ceiling(log_4(maxSizeHint))" *) t.maxLevel := 0; WHILE maxSizeHint > 0 DO maxSizeHint := Word.RightShift(maxSizeHint, 2); INC(t.maxLevel) END; t.maxLevel := MAX(t.maxLevel, 1); (* initialized "t.root" *) IF t.root = NIL THEN t.root := NEW(Node) END; IF t.root.forward = NIL OR NUMBER(t.root.forward^) # t.maxLevel THEN t.root.forward := NEW(ForwardPtrs, t.maxLevel); t.update := NEW(ForwardPtrs, t.maxLevel); END; t.root.back := NIL; FOR i := 0 TO t.maxLevel - 1 DO t.root.forward[i] := NIL END; (* initialize random number generator *) IF fixedSeed OR t.rand = NIL THEN t.rand := NEW(Random.Default).init(fixedSeed) END; t.randBits := t.rand.integer(); t.bitsRem := Word.Size; <* ASSERT (t.bitsRem MOD 4) = 0 *> (* initialize other fields *) t.num := 0; t.level := 0; RETURN t END Init; PROCEDURESize (t: T): CARDINAL = BEGIN RETURN t.num END Size; PROCEDUREGet (t: T; READONLY k: Key.T; VAR (*OUT*) v: Value.T): BOOLEAN = VAR alreadyChecked: Node := NIL; n := t.root; BEGIN FOR i := t.level - 1 TO 0 BY -1 DO VAR next := n.forward[i]; BEGIN WHILE next # alreadyChecked AND next # NIL AND t.keyCompare(next.k, k) < 0 DO n := next; next := n.forward[i] END; alreadyChecked := next END END; n := n.forward[0]; IF n # NIL AND t.keyCompare(n.k, k) = 0 THEN v := n.v; RETURN TRUE END; RETURN FALSE END Get; PROCEDUREPut (t: T; READONLY k: Key.T; READONLY v: Value.T): BOOLEAN = VAR alreadyChecked: Node := NIL; n := t.root; BEGIN FOR i := t.level - 1 TO 0 BY -1 DO VAR next := n.forward[i]; BEGIN WHILE next # alreadyChecked AND next # NIL AND t.keyCompare(next.k, k) < 0 DO n := next; next := n.forward[i] END; alreadyChecked := next; t.update[i] := n END END; n := n.forward[0]; IF n # NIL AND t.keyCompare(n.k, k) = 0 THEN (* key already in table; set its value *) n.v := v; RETURN TRUE END; VAR newLevel := RandLevel(t); newNode := NewNode(newLevel, k, v); BEGIN (* correct "update" array if new level is new max *) IF newLevel > t.level THEN FOR i := t.level TO newLevel - 1 DO t.update[i] := t.root END; t.level := newLevel END; (* update forward pointers *) FOR i := 0 TO newLevel - 1 DO WITH prev = t.update[i].forward[i] DO newNode.forward[i] := prev; prev := newNode END END; (* update back pointers *) newNode.back := t.update[0]; VAR nextNode := newNode.forward[0]; BEGIN IF nextNode = NIL THEN t.root.back := newNode ELSE nextNode.back := newNode END END END; INC(t.num); RETURN FALSE END Put; PROCEDUREDelete (t: T; READONLY k: Key.T; VAR (*OUT*) v: Value.T): BOOLEAN = VAR alreadyChecked: Node := NIL; n := t.root; BEGIN FOR i := t.level - 1 TO 0 BY -1 DO VAR next := n.forward[i]; BEGIN WHILE next # alreadyChecked AND next # NIL AND t.keyCompare(next.k, k) < 0 DO n := next; next := n.forward[i] END; alreadyChecked := next; t.update[i] := n END END; n := n.forward[0]; IF n = NIL OR t.keyCompare(n.k, k) # 0 THEN (* key not in table *) RETURN FALSE END; v := n.v; (* update forward pointers *) FOR i := 0 TO t.level - 1 DO WITH prevPtr = t.update[i].forward[i] DO IF prevPtr # n THEN EXIT END; prevPtr := n.forward[i] END END; (* update back pointers *) VAR nextNode := n.forward[0]; BEGIN IF nextNode = NIL THEN t.root.back := n.back ELSE nextNode.back := n.back END END; DEC(t.num); RETURN TRUE END Delete; PROCEDUREIterate (t: T): Tbl.Iterator = BEGIN RETURN IterateOrdered(t, TRUE) END Iterate; PROCEDUREIterateOrdered (t: T; up: BOOLEAN): SortedTbl.Iterator = VAR res: Iterator; BEGIN IF up THEN res := NEW(IteratorUp); ELSE res := NEW(IteratorDown); END; res.t := t; res.reset(); RETURN res END IterateOrdered;PROCEDUREResetUp (it: Iterator) = BEGIN it.curr := it.t.root.forward[0] END ResetUp; PROCEDUREResetDown (it: Iterator) = BEGIN it.curr := it.t.root.back END ResetDown; PROCEDURENextUp (it: Iterator; VAR (*OUT*) k: Key.T; VAR (*OUT*) v: Value.T): BOOLEAN = VAR curr := it.curr; BEGIN IF curr = NIL THEN RETURN FALSE END; k := curr.k; v := curr.v; it.curr := curr.forward[0]; RETURN TRUE; END NextUp; PROCEDURENextDown (it: Iterator; VAR (*OUT*) k: Key.T; VAR (*OUT*) v: Value.T): BOOLEAN = VAR curr := it.curr; BEGIN IF curr = it.t.root THEN RETURN FALSE END; k := curr.k; v := curr.v; it.curr := curr.back; RETURN TRUE; END NextDown; PROCEDURESeekUp (it: Iterator; READONLY key: Key.T) = VAR n := Seek(it.t, key); BEGIN it.curr := n.forward[0] END SeekUp; PROCEDURESeekDown (it: Iterator; READONLY key: Key.T) = VAR n := Seek(it.t, key); next := n.forward[0]; BEGIN IF next # NIL AND it.t.keyCompare(next.k, key) = 0 THEN it.curr := next ELSE it.curr := n END END SeekDown; BEGIN END SkipListTbl.