You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							571 lines
						
					
					
						
							13 KiB
						
					
					
				
			
		
		
		
			
			
			
		
		
	
	
							571 lines
						
					
					
						
							13 KiB
						
					
					
				
								package skiplist
							 | 
						|
								
							 | 
						|
								// adapted from https://github.com/MauriceGit/skiplist/blob/master/skiplist.go
							 | 
						|
								
							 | 
						|
								import (
							 | 
						|
									"bytes"
							 | 
						|
									"fmt"
							 | 
						|
									"math/bits"
							 | 
						|
									"math/rand"
							 | 
						|
									"time"
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								const (
							 | 
						|
									// maxLevel denotes the maximum height of the skiplist. This height will keep the skiplist
							 | 
						|
									// efficient for up to 34m entries. If there is a need for much more, please adjust this constant accordingly.
							 | 
						|
									maxLevel = 25
							 | 
						|
								)
							 | 
						|
								
							 | 
						|
								type SkipList struct {
							 | 
						|
									StartLevels [maxLevel]*SkipListElementReference
							 | 
						|
									EndLevels   [maxLevel]*SkipListElementReference
							 | 
						|
									MaxNewLevel int
							 | 
						|
									MaxLevel    int
							 | 
						|
									ListStore   ListStore
							 | 
						|
									HasChanges  bool
							 | 
						|
									// elementCount int
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// NewSeed returns a new empty, initialized Skiplist.
							 | 
						|
								// Given a seed, a deterministic height/list behaviour can be achieved.
							 | 
						|
								// Eps is used to compare keys given by the ExtractKey() function on equality.
							 | 
						|
								func NewSeed(seed int64, listStore ListStore) *SkipList {
							 | 
						|
								
							 | 
						|
									// Initialize random number generator.
							 | 
						|
									rand.Seed(seed)
							 | 
						|
									//fmt.Printf("SkipList seed: %v\n", seed)
							 | 
						|
								
							 | 
						|
									list := &SkipList{
							 | 
						|
										MaxNewLevel: maxLevel,
							 | 
						|
										MaxLevel:    0,
							 | 
						|
										ListStore:   listStore,
							 | 
						|
										// elementCount: 0,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return list
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// New returns a new empty, initialized Skiplist.
							 | 
						|
								func New(listStore ListStore) *SkipList {
							 | 
						|
									return NewSeed(time.Now().UTC().UnixNano(), listStore)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// IsEmpty checks, if the skiplist is empty.
							 | 
						|
								func (t *SkipList) IsEmpty() bool {
							 | 
						|
									return t.StartLevels[0] == nil
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (t *SkipList) generateLevel(maxLevel int) int {
							 | 
						|
									level := maxLevel - 1
							 | 
						|
									// First we apply some mask which makes sure that we don't get a level
							 | 
						|
									// above our desired level. Then we find the first set bit.
							 | 
						|
									var x = rand.Uint64() & ((1 << uint(maxLevel-1)) - 1)
							 | 
						|
									zeroes := bits.TrailingZeros64(x)
							 | 
						|
									if zeroes <= maxLevel {
							 | 
						|
										level = zeroes
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									return level
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (t *SkipList) findEntryIndex(key []byte, minLevel int) int {
							 | 
						|
									// Find good entry point so we don't accidentally skip half the list.
							 | 
						|
									for i := t.MaxLevel; i >= 0; i-- {
							 | 
						|
										if t.StartLevels[i] != nil && bytes.Compare(t.StartLevels[i].Key, key) < 0 || i <= minLevel {
							 | 
						|
											return i
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									return 0
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								func (t *SkipList) findExtended(key []byte, findGreaterOrEqual bool) (prevElementIfVisited *SkipListElement, foundElem *SkipListElement, ok bool, err error) {
							 | 
						|
								
							 | 
						|
									foundElem = nil
							 | 
						|
									ok = false
							 | 
						|
								
							 | 
						|
									if t.IsEmpty() {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									index := t.findEntryIndex(key, 0)
							 | 
						|
									var currentNode *SkipListElement
							 | 
						|
								
							 | 
						|
									currentNode, err = t.LoadElement(t.StartLevels[index])
							 | 
						|
									if err != nil {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
									if currentNode == nil {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// In case, that our first element is already greater-or-equal!
							 | 
						|
									if findGreaterOrEqual && compareElement(currentNode, key) > 0 {
							 | 
						|
										foundElem = currentNode
							 | 
						|
										ok = true
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									for {
							 | 
						|
										if compareElement(currentNode, key) == 0 {
							 | 
						|
											foundElem = currentNode
							 | 
						|
											ok = true
							 | 
						|
											return
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Which direction are we continuing next time?
							 | 
						|
										if currentNode.Next[index] != nil && bytes.Compare(currentNode.Next[index].Key, key) <= 0 {
							 | 
						|
											// Go right
							 | 
						|
											currentNode, err = t.LoadElement(currentNode.Next[index])
							 | 
						|
											if err != nil {
							 | 
						|
												return
							 | 
						|
											}
							 | 
						|
											if currentNode == nil {
							 | 
						|
												return
							 | 
						|
											}
							 | 
						|
										} else {
							 | 
						|
											if index > 0 {
							 | 
						|
								
							 | 
						|
												// Early exit
							 | 
						|
												if currentNode.Next[0] != nil && bytes.Compare(currentNode.Next[0].Key, key) == 0 {
							 | 
						|
													prevElementIfVisited = currentNode
							 | 
						|
													var currentNodeNext *SkipListElement
							 | 
						|
													currentNodeNext, err = t.LoadElement(currentNode.Next[0])
							 | 
						|
													if err != nil {
							 | 
						|
														return
							 | 
						|
													}
							 | 
						|
													if currentNodeNext == nil {
							 | 
						|
														return
							 | 
						|
													}
							 | 
						|
													foundElem = currentNodeNext
							 | 
						|
													ok = true
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
												// Go down
							 | 
						|
												index--
							 | 
						|
											} else {
							 | 
						|
												// Element is not found and we reached the bottom.
							 | 
						|
												if findGreaterOrEqual {
							 | 
						|
													foundElem, err = t.LoadElement(currentNode.Next[index])
							 | 
						|
													if err != nil {
							 | 
						|
														return
							 | 
						|
													}
							 | 
						|
													ok = foundElem != nil
							 | 
						|
												}
							 | 
						|
								
							 | 
						|
												return
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Find tries to find an element in the skiplist based on the key from the given ListElement.
							 | 
						|
								// elem can be used, if ok is true.
							 | 
						|
								// Find runs in approx. O(log(n))
							 | 
						|
								func (t *SkipList) Find(key []byte) (prevIfVisited *SkipListElement, elem *SkipListElement, ok bool, err error) {
							 | 
						|
								
							 | 
						|
									if t == nil || key == nil {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									prevIfVisited, elem, ok, err = t.findExtended(key, false)
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// FindGreaterOrEqual finds the first element, that is greater or equal to the given ListElement e.
							 | 
						|
								// The comparison is done on the keys (So on ExtractKey()).
							 | 
						|
								// FindGreaterOrEqual runs in approx. O(log(n))
							 | 
						|
								func (t *SkipList) FindGreaterOrEqual(key []byte) (prevIfVisited *SkipListElement, elem *SkipListElement, ok bool, err error) {
							 | 
						|
								
							 | 
						|
									if t == nil || key == nil {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									prevIfVisited, elem, ok, err = t.findExtended(key, true)
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Delete removes an element equal to e from the skiplist, if there is one.
							 | 
						|
								// If there are multiple entries with the same value, Delete will remove one of them
							 | 
						|
								// (Which one will change based on the actual skiplist layout)
							 | 
						|
								// Delete runs in approx. O(log(n))
							 | 
						|
								func (t *SkipList) DeleteByKey(key []byte) (id int64, err error) {
							 | 
						|
								
							 | 
						|
									if t == nil || t.IsEmpty() || key == nil {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									index := t.findEntryIndex(key, t.MaxLevel)
							 | 
						|
								
							 | 
						|
									var currentNode *SkipListElement
							 | 
						|
									var nextNode *SkipListElement
							 | 
						|
								
							 | 
						|
									for {
							 | 
						|
								
							 | 
						|
										if currentNode == nil {
							 | 
						|
											nextNode, err = t.LoadElement(t.StartLevels[index])
							 | 
						|
										} else {
							 | 
						|
											nextNode, err = t.LoadElement(currentNode.Next[index])
							 | 
						|
										}
							 | 
						|
										if err != nil {
							 | 
						|
											return id, err
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										// Found and remove!
							 | 
						|
										if nextNode != nil && compareElement(nextNode, key) == 0 {
							 | 
						|
								
							 | 
						|
											if currentNode != nil {
							 | 
						|
												currentNode.Next[index] = nextNode.Next[index]
							 | 
						|
												if err = t.SaveElement(currentNode); err != nil {
							 | 
						|
													return id, err
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											if index == 0 {
							 | 
						|
												if nextNode.Next[index] != nil {
							 | 
						|
													nextNextNode, err := t.LoadElement(nextNode.Next[index])
							 | 
						|
													if err != nil {
							 | 
						|
														return id, err
							 | 
						|
													}
							 | 
						|
													if nextNextNode != nil {
							 | 
						|
														nextNextNode.Prev = currentNode.Reference()
							 | 
						|
														if err = t.SaveElement(nextNextNode); err != nil {
							 | 
						|
															return id, err
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
												// t.elementCount--
							 | 
						|
												id = nextNode.Id
							 | 
						|
												if err = t.DeleteElement(nextNode); err != nil {
							 | 
						|
													return id, err
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// Link from start needs readjustments.
							 | 
						|
											startNextKey := t.StartLevels[index].Key
							 | 
						|
											if compareElement(nextNode, startNextKey) == 0 {
							 | 
						|
												t.HasChanges = true
							 | 
						|
												t.StartLevels[index] = nextNode.Next[index]
							 | 
						|
												// This was our currently highest node!
							 | 
						|
												if t.StartLevels[index] == nil {
							 | 
						|
													t.MaxLevel = index - 1
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// Link from end needs readjustments.
							 | 
						|
											if nextNode.Next[index] == nil {
							 | 
						|
												t.EndLevels[index] = currentNode.Reference()
							 | 
						|
												t.HasChanges = true
							 | 
						|
											}
							 | 
						|
											nextNode.Next[index] = nil
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if nextNode != nil && compareElement(nextNode, key) < 0 {
							 | 
						|
											// Go right
							 | 
						|
											currentNode = nextNode
							 | 
						|
										} else {
							 | 
						|
											// Go down
							 | 
						|
											index--
							 | 
						|
											if index < 0 {
							 | 
						|
												break
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
									return
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Insert inserts the given ListElement into the skiplist.
							 | 
						|
								// Insert runs in approx. O(log(n))
							 | 
						|
								func (t *SkipList) InsertByKey(key []byte, idIfKnown int64, value []byte) (id int64, err error) {
							 | 
						|
								
							 | 
						|
									if t == nil || key == nil {
							 | 
						|
										return
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									level := t.generateLevel(t.MaxNewLevel)
							 | 
						|
								
							 | 
						|
									// Only grow the height of the skiplist by one at a time!
							 | 
						|
									if level > t.MaxLevel {
							 | 
						|
										level = t.MaxLevel + 1
							 | 
						|
										t.MaxLevel = level
							 | 
						|
										t.HasChanges = true
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									id = idIfKnown
							 | 
						|
									if id == 0 {
							 | 
						|
										id = rand.Int63()
							 | 
						|
									}
							 | 
						|
									elem := &SkipListElement{
							 | 
						|
										Id:    id,
							 | 
						|
										Next:  make([]*SkipListElementReference, t.MaxNewLevel, t.MaxNewLevel),
							 | 
						|
										Level: int32(level),
							 | 
						|
										Key:   key,
							 | 
						|
										Value: value,
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// t.elementCount++
							 | 
						|
								
							 | 
						|
									newFirst := true
							 | 
						|
									newLast := true
							 | 
						|
									if !t.IsEmpty() {
							 | 
						|
										newFirst = compareElement(elem, t.StartLevels[0].Key) < 0
							 | 
						|
										newLast = compareElement(elem, t.EndLevels[0].Key) > 0
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									normallyInserted := false
							 | 
						|
									if !newFirst && !newLast {
							 | 
						|
								
							 | 
						|
										normallyInserted = true
							 | 
						|
								
							 | 
						|
										index := t.findEntryIndex(key, level)
							 | 
						|
								
							 | 
						|
										var currentNode *SkipListElement
							 | 
						|
										var nextNodeRef *SkipListElementReference
							 | 
						|
								
							 | 
						|
										for {
							 | 
						|
								
							 | 
						|
											if currentNode == nil {
							 | 
						|
												nextNodeRef = t.StartLevels[index]
							 | 
						|
											} else {
							 | 
						|
												nextNodeRef = currentNode.Next[index]
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											var nextNode *SkipListElement
							 | 
						|
								
							 | 
						|
											// Connect node to next
							 | 
						|
											if index <= level && (nextNodeRef == nil || bytes.Compare(nextNodeRef.Key, key) > 0) {
							 | 
						|
												elem.Next[index] = nextNodeRef
							 | 
						|
												if currentNode != nil {
							 | 
						|
													currentNode.Next[index] = elem.Reference()
							 | 
						|
													if err = t.SaveElement(currentNode); err != nil {
							 | 
						|
														return
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
												if index == 0 {
							 | 
						|
													elem.Prev = currentNode.Reference()
							 | 
						|
													if nextNodeRef != nil {
							 | 
						|
														if nextNode, err = t.LoadElement(nextNodeRef); err != nil {
							 | 
						|
															return
							 | 
						|
														}
							 | 
						|
														if nextNode != nil {
							 | 
						|
															nextNode.Prev = elem.Reference()
							 | 
						|
															if err = t.SaveElement(nextNode); err != nil {
							 | 
						|
																return
							 | 
						|
															}
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											if nextNodeRef != nil && bytes.Compare(nextNodeRef.Key, key) <= 0 {
							 | 
						|
												// Go right
							 | 
						|
												if nextNode == nil {
							 | 
						|
													// reuse nextNode when index == 0
							 | 
						|
													if nextNode, err = t.LoadElement(nextNodeRef); err != nil {
							 | 
						|
														return
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
												currentNode = nextNode
							 | 
						|
												if currentNode == nil {
							 | 
						|
													return
							 | 
						|
												}
							 | 
						|
											} else {
							 | 
						|
												// Go down
							 | 
						|
												index--
							 | 
						|
												if index < 0 {
							 | 
						|
													break
							 | 
						|
												}
							 | 
						|
											}
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									// Where we have a left-most position that needs to be referenced!
							 | 
						|
									for i := level; i >= 0; i-- {
							 | 
						|
								
							 | 
						|
										didSomething := false
							 | 
						|
								
							 | 
						|
										if newFirst || normallyInserted {
							 | 
						|
								
							 | 
						|
											if t.StartLevels[i] == nil || bytes.Compare(t.StartLevels[i].Key, key) > 0 {
							 | 
						|
												if i == 0 && t.StartLevels[i] != nil {
							 | 
						|
													startLevelElement, err := t.LoadElement(t.StartLevels[i])
							 | 
						|
													if err != nil {
							 | 
						|
														return id, err
							 | 
						|
													}
							 | 
						|
													if startLevelElement != nil {
							 | 
						|
														startLevelElement.Prev = elem.Reference()
							 | 
						|
														if err = t.SaveElement(startLevelElement); err != nil {
							 | 
						|
															return id, err
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
												elem.Next[i] = t.StartLevels[i]
							 | 
						|
												t.StartLevels[i] = elem.Reference()
							 | 
						|
												t.HasChanges = true
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// link the EndLevels to this element!
							 | 
						|
											if elem.Next[i] == nil {
							 | 
						|
												t.EndLevels[i] = elem.Reference()
							 | 
						|
												t.HasChanges = true
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											didSomething = true
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if newLast {
							 | 
						|
											// Places the element after the very last element on this level!
							 | 
						|
											// This is very important, so we are not linking the very first element (newFirst AND newLast) to itself!
							 | 
						|
											if !newFirst {
							 | 
						|
												if t.EndLevels[i] != nil {
							 | 
						|
													endLevelElement, err := t.LoadElement(t.EndLevels[i])
							 | 
						|
													if err != nil {
							 | 
						|
														return id, err
							 | 
						|
													}
							 | 
						|
													if endLevelElement != nil {
							 | 
						|
														endLevelElement.Next[i] = elem.Reference()
							 | 
						|
														if err = t.SaveElement(endLevelElement); err != nil {
							 | 
						|
															return id, err
							 | 
						|
														}
							 | 
						|
													}
							 | 
						|
												}
							 | 
						|
												if i == 0 {
							 | 
						|
													elem.Prev = t.EndLevels[i]
							 | 
						|
												}
							 | 
						|
												t.EndLevels[i] = elem.Reference()
							 | 
						|
												t.HasChanges = true
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											// Link the startLevels to this element!
							 | 
						|
											if t.StartLevels[i] == nil || bytes.Compare(t.StartLevels[i].Key, key) > 0 {
							 | 
						|
												t.StartLevels[i] = elem.Reference()
							 | 
						|
												t.HasChanges = true
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											didSomething = true
							 | 
						|
										}
							 | 
						|
								
							 | 
						|
										if !didSomething {
							 | 
						|
											break
							 | 
						|
										}
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									if err = t.SaveElement(elem); err != nil {
							 | 
						|
										return id, err
							 | 
						|
									}
							 | 
						|
									return id, nil
							 | 
						|
								
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GetSmallestNode returns the very first/smallest node in the skiplist.
							 | 
						|
								// GetSmallestNode runs in O(1)
							 | 
						|
								func (t *SkipList) GetSmallestNode() (*SkipListElement, error) {
							 | 
						|
									return t.LoadElement(t.StartLevels[0])
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// GetLargestNode returns the very last/largest node in the skiplist.
							 | 
						|
								// GetLargestNode runs in O(1)
							 | 
						|
								func (t *SkipList) GetLargestNode() (*SkipListElement, error) {
							 | 
						|
									return t.LoadElement(t.EndLevels[0])
							 | 
						|
								}
							 | 
						|
								func (t *SkipList) GetLargestNodeReference() *SkipListElementReference {
							 | 
						|
									return t.EndLevels[0]
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Next returns the next element based on the given node.
							 | 
						|
								// Next will loop around to the first node, if you call it on the last!
							 | 
						|
								func (t *SkipList) Next(e *SkipListElement) (*SkipListElement, error) {
							 | 
						|
									if e.Next[0] == nil {
							 | 
						|
										return t.LoadElement(t.StartLevels[0])
							 | 
						|
									}
							 | 
						|
									return t.LoadElement(e.Next[0])
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// Prev returns the previous element based on the given node.
							 | 
						|
								// Prev will loop around to the last node, if you call it on the first!
							 | 
						|
								func (t *SkipList) Prev(e *SkipListElement) (*SkipListElement, error) {
							 | 
						|
									if e.Prev == nil {
							 | 
						|
										return t.LoadElement(t.EndLevels[0])
							 | 
						|
									}
							 | 
						|
									return t.LoadElement(e.Prev)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// ChangeValue can be used to change the actual value of a node in the skiplist
							 | 
						|
								// without the need of Deleting and reinserting the node again.
							 | 
						|
								// Be advised, that ChangeValue only works, if the actual key from ExtractKey() will stay the same!
							 | 
						|
								// ok is an indicator, wether the value is actually changed.
							 | 
						|
								func (t *SkipList) ChangeValue(e *SkipListElement, newValue []byte) (err error) {
							 | 
						|
									// The key needs to stay correct, so this is very important!
							 | 
						|
									e.Value = newValue
							 | 
						|
									return t.SaveElement(e)
							 | 
						|
								}
							 | 
						|
								
							 | 
						|
								// String returns a string format of the skiplist. Useful to get a graphical overview and/or debugging.
							 | 
						|
								func (t *SkipList) println() {
							 | 
						|
								
							 | 
						|
									print("start --> ")
							 | 
						|
									for i, l := range t.StartLevels {
							 | 
						|
										if l == nil {
							 | 
						|
											break
							 | 
						|
										}
							 | 
						|
										if i > 0 {
							 | 
						|
											print(" -> ")
							 | 
						|
										}
							 | 
						|
										next := "---"
							 | 
						|
										if l != nil {
							 | 
						|
											next = string(l.Key)
							 | 
						|
										}
							 | 
						|
										print(fmt.Sprintf("[%v]", next))
							 | 
						|
									}
							 | 
						|
									println()
							 | 
						|
								
							 | 
						|
									nodeRef := t.StartLevels[0]
							 | 
						|
									for nodeRef != nil {
							 | 
						|
										print(fmt.Sprintf("%v: ", string(nodeRef.Key)))
							 | 
						|
										node, _ := t.LoadElement(nodeRef)
							 | 
						|
										if node == nil {
							 | 
						|
											break
							 | 
						|
										}
							 | 
						|
										for i := 0; i <= int(node.Level); i++ {
							 | 
						|
								
							 | 
						|
											l := node.Next[i]
							 | 
						|
								
							 | 
						|
											next := "---"
							 | 
						|
											if l != nil {
							 | 
						|
												next = string(l.Key)
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
											if i == 0 {
							 | 
						|
												prev := "---"
							 | 
						|
								
							 | 
						|
												if node.Prev != nil {
							 | 
						|
													prev = string(node.Prev.Key)
							 | 
						|
												}
							 | 
						|
												print(fmt.Sprintf("[%v|%v]", prev, next))
							 | 
						|
											} else {
							 | 
						|
												print(fmt.Sprintf("[%v]", next))
							 | 
						|
											}
							 | 
						|
											if i < int(node.Level) {
							 | 
						|
												print(" -> ")
							 | 
						|
											}
							 | 
						|
								
							 | 
						|
										}
							 | 
						|
										nodeRef = node.Next[0]
							 | 
						|
										println()
							 | 
						|
									}
							 | 
						|
								
							 | 
						|
									print("end --> ")
							 | 
						|
									for i, l := range t.EndLevels {
							 | 
						|
										if l == nil {
							 | 
						|
											break
							 | 
						|
										}
							 | 
						|
										if i > 0 {
							 | 
						|
											print(" -> ")
							 | 
						|
										}
							 | 
						|
										next := "---"
							 | 
						|
										if l != nil {
							 | 
						|
											next = string(l.Key)
							 | 
						|
										}
							 | 
						|
										print(fmt.Sprintf("[%v]", next))
							 | 
						|
									}
							 | 
						|
									println()
							 | 
						|
								}
							 |