github.com/lazyledger/lazyledger-core@v0.35.0-dev.0.20210613111200-4c651f053571/libs/db/memdb/iterator.go (about) 1 package memdb 2 3 import ( 4 "bytes" 5 "context" 6 7 "github.com/google/btree" 8 9 lldb "github.com/lazyledger/lazyledger-core/libs/db" 10 ) 11 12 const ( 13 // Size of the channel buffer between traversal goroutine and iterator. Using an unbuffered 14 // channel causes two context switches per item sent, while buffering allows more work per 15 // context switch. Tuned with benchmarks. 16 chBufferSize = 64 17 ) 18 19 // memDBIterator is a memDB iterator. 20 type memDBIterator struct { 21 ch <-chan *item 22 cancel context.CancelFunc 23 item *item 24 start []byte 25 end []byte 26 } 27 28 var _ lldb.Iterator = (*memDBIterator)(nil) 29 30 // newMemDBIterator creates a new memDBIterator. 31 func newMemDBIterator(db *MemDB, start []byte, end []byte, reverse bool) *memDBIterator { 32 ctx, cancel := context.WithCancel(context.Background()) 33 ch := make(chan *item, chBufferSize) 34 iter := &memDBIterator{ 35 ch: ch, 36 cancel: cancel, 37 start: start, 38 end: end, 39 } 40 41 db.mtx.RLock() 42 go func() { 43 defer db.mtx.RUnlock() 44 // Because we use [start, end) for reverse ranges, while btree uses (start, end], we need 45 // the following variables to handle some reverse iteration conditions ourselves. 46 var ( 47 skipEqual []byte 48 abortLessThan []byte 49 ) 50 visitor := func(i btree.Item) bool { 51 item := i.(*item) 52 if skipEqual != nil && bytes.Equal(item.key, skipEqual) { 53 skipEqual = nil 54 return true 55 } 56 if abortLessThan != nil && bytes.Compare(item.key, abortLessThan) == -1 { 57 return false 58 } 59 select { 60 case <-ctx.Done(): 61 return false 62 case ch <- item: 63 return true 64 } 65 } 66 switch { 67 case start == nil && end == nil && !reverse: 68 db.btree.Ascend(visitor) 69 case start == nil && end == nil && reverse: 70 db.btree.Descend(visitor) 71 case end == nil && !reverse: 72 // must handle this specially, since nil is considered less than anything else 73 db.btree.AscendGreaterOrEqual(newKey(start), visitor) 74 case !reverse: 75 db.btree.AscendRange(newKey(start), newKey(end), visitor) 76 case end == nil: 77 // abort after start, since we use [start, end) while btree uses (start, end] 78 abortLessThan = start 79 db.btree.Descend(visitor) 80 default: 81 // skip end and abort after start, since we use [start, end) while btree uses (start, end] 82 skipEqual = end 83 abortLessThan = start 84 db.btree.DescendLessOrEqual(newKey(end), visitor) 85 } 86 close(ch) 87 }() 88 89 // prime the iterator with the first value, if any 90 if item, ok := <-ch; ok { 91 iter.item = item 92 } 93 94 return iter 95 } 96 97 // Close implements Iterator. 98 func (i *memDBIterator) Close() error { 99 i.cancel() 100 for range i.ch { // drain channel 101 } 102 i.item = nil 103 return nil 104 } 105 106 // Domain implements Iterator. 107 func (i *memDBIterator) Domain() ([]byte, []byte) { 108 return i.start, i.end 109 } 110 111 // Valid implements Iterator. 112 func (i *memDBIterator) Valid() bool { 113 return i.item != nil 114 } 115 116 // Next implements Iterator. 117 func (i *memDBIterator) Next() { 118 i.assertIsValid() 119 item, ok := <-i.ch 120 switch { 121 case ok: 122 i.item = item 123 default: 124 i.item = nil 125 } 126 } 127 128 // Error implements Iterator. 129 func (i *memDBIterator) Error() error { 130 return nil // famous last words 131 } 132 133 // Key implements Iterator. 134 func (i *memDBIterator) Key() []byte { 135 i.assertIsValid() 136 return i.item.key 137 } 138 139 // Value implements Iterator. 140 func (i *memDBIterator) Value() []byte { 141 i.assertIsValid() 142 return i.item.value 143 } 144 145 func (i *memDBIterator) assertIsValid() { 146 if !i.Valid() { 147 panic("iterator is invalid") 148 } 149 }