Browse Source

upgrade buntdb

tags/v2.8.0-rc1
Shivaram Lingamneni 2 years ago
parent
commit
c5a9916302

+ 1
- 1
go.mod View File

@@ -17,7 +17,7 @@ require (
17 17
 	github.com/onsi/ginkgo v1.12.0 // indirect
18 18
 	github.com/onsi/gomega v1.9.0 // indirect
19 19
 	github.com/stretchr/testify v1.4.0 // indirect
20
-	github.com/tidwall/buntdb v1.2.3
20
+	github.com/tidwall/buntdb v1.2.6
21 21
 	github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208
22 22
 	github.com/xdg-go/scram v1.0.2
23 23
 	golang.org/x/crypto v0.0.0-20210415154028-4f45737414dc

+ 8
- 0
go.sum View File

@@ -41,12 +41,20 @@ github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJy
41 41
 github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
42 42
 github.com/tidwall/btree v0.4.2 h1:aLwwJlG+InuFzdAPuBf9YCAR1LvSQ9zhC5aorFPlIPs=
43 43
 github.com/tidwall/btree v0.4.2/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
44
+github.com/tidwall/btree v0.6.0 h1:JLYAFGV+1gjyFi3iQbO/fupBin+Ooh7dxqVV0twJ1Bo=
45
+github.com/tidwall/btree v0.6.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
44 46
 github.com/tidwall/buntdb v1.2.3 h1:AoGVe4yrhKmnEPHrPrW5EUOATHOCIk4VtFvd8xn/ZtU=
45 47
 github.com/tidwall/buntdb v1.2.3/go.mod h1:+i/gBwYOHWG19wLgwMXFLkl00twh9+VWkkaOhuNQ4PA=
48
+github.com/tidwall/buntdb v1.2.6 h1:eS0QSmzHfCKjxxYGh8eH6wnK5VLsJ7UjyyIr29JmnEg=
49
+github.com/tidwall/buntdb v1.2.6/go.mod h1:zpXqlA5D2772I4cTqV3ifr2AZihDgi8FV7xAQu6edfc=
46 50
 github.com/tidwall/gjson v1.7.4 h1:19cchw8FOxkG5mdLRkGf9jqIqEyqdZhPqW60XfyFxk8=
47 51
 github.com/tidwall/gjson v1.7.4/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
52
+github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ=
53
+github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
48 54
 github.com/tidwall/grect v0.1.1 h1:+kMEkxhoqB7rniVXzMEIA66XwU07STgINqxh+qVIndY=
49 55
 github.com/tidwall/grect v0.1.1/go.mod h1:CzvbGiFbWUwiJ1JohXLb28McpyBsI00TK9Y6pDWLGRQ=
56
+github.com/tidwall/grect v0.1.2 h1:wKVeQVZhjaFCKTTlpkDe3Ex4ko3cMGW3MRKawRe8uQ4=
57
+github.com/tidwall/grect v0.1.2/go.mod h1:v+n4ewstPGduVJebcp5Eh2WXBJBumNzyhK8GZt4gHNw=
50 58
 github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8=
51 59
 github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8=
52 60
 github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=

+ 43
- 0
vendor/github.com/tidwall/btree/PATH_HINT.md View File

@@ -0,0 +1,43 @@
1
+# B-tree Path Hints
2
+
3
+I use a thing I call path hints in my B-tree [C](https://github.com/tidwall/btree.c) and [Go](https://github.com/tidwall/btree) implementations. It's a search optimization.
4
+
5
+## The B-tree
6
+
7
+A standard [B-tree](https://en.wikipedia.org/wiki/B-tree) is an ordered tree-based data structure that stores its items in nodes. The B-tree has a single root node, which may have children nodes, and those children nodes may also have children nodes. 
8
+
9
+<img width="322" alt="image" src="https://user-images.githubusercontent.com/1156077/127664015-14ca38bb-1a3b-4d2f-80ff-27be0bd3d886.png">
10
+
11
+Searching for items in a B-tree is fast. [O(log N)](https://en.wikipedia.org/wiki/Big_O_notation) to be exact.
12
+This is because the [binary search algorithm](https://en.wikipedia.org/wiki/Binary_search_algorithm) is used. 
13
+
14
+A binary search works by first comparing the item at the middle-most index of the root node with the target item. 
15
+If the middle item is greater than the target item, then it divides the node in two and does the binary search on the left part of the node. If the middle is less, it searches the right part. And so on. If the target item is found, then the search stop. If the item is not found, then the search is passed to the child node at the appropriate index. This traversal terminates when item is found or there are no more child nodes.
16
+
17
+<img width="600" alt="image" src="https://user-images.githubusercontent.com/1156077/127664822-6ab4f8f6-8ab5-477e-8e17-f52346f02819.png">
18
+
19
+## The Path
20
+
21
+Each index is a component of the path to the item (or where the item should be stored, if it does not exist in the tree).
22
+
23
+Take the first example image. The item 9 is at path “1/0”. The item 16 is at path “1”. The item 21 is at path “2/1”. The item 5 is at path “0/2”.
24
+
25
+## The Path Hint
26
+
27
+A Path Hint is a predefined path that is provided to B-tree operations. It’s just a hint that says, “Hey B-tree, instead of starting your binary search with the middle index, start with what I provide you. My path may be wrong, and if so please provide me with the correct path so I get it right the next time.”
28
+
29
+I’ve found using path hints can lead to a little performance increase of 150% - 300%. This is because in real-world cases the items that I’m working with are usually nearby each other in the tree. 
30
+
31
+Take for example inserting a group of timeseries points. They may often be received as chucks of near-contiguous items.  
32
+Or, I'm sequentially inserting an ordered group of rows somewhere in the middle of a table.  
33
+Or, I have a Redis-style key/value store, where the keys look have the common structure “user:98512:name”, “user:98512:email”, and I want to update a bunch of values for specified user.  
34
+Using a path hint may help to avoid the unnecessary binary searching in each of these examples.
35
+
36
+While I may see a 3x boost in when the path hint is right on, I'll only see around 5% decrease when the path hint is totally wrong.
37
+
38
+## Using a Path Hint
39
+
40
+For single-threaded programs, it’s possible to use one shared path hint per B-tree for the life of the program.  
41
+For multi-threaded programs, I find it best to use one path hint per B-tree , per thread.  
42
+For server-client programs, one path hint per B-tree, per client should suffice.  
43
+

+ 19
- 19
vendor/github.com/tidwall/btree/README.md View File

@@ -9,7 +9,7 @@ An [efficient](#performance) [B-tree](https://en.wikipedia.org/wiki/B-tree) impl
9 9
 - `Copy()` method with copy-on-write support.
10 10
 - Fast bulk loading for pre-ordered data using the `Load()` method.
11 11
 - All operations are thread-safe.
12
-- Path hinting optimization for operations with nearby keys.
12
+- [Path hinting](PATH_HINT.md) optimization for operations with nearby keys.
13 13
 
14 14
 ## Installing
15 15
 
@@ -155,7 +155,7 @@ DeleteHint(item, *hint) # delete an item
155 155
 
156 156
 This implementation was designed with performance in mind. 
157 157
 
158
-The following benchmarks were run on my 2019 Macbook Pro (2.4 GHz 8-Core Intel Core i9) using Go 1.15.3. The items are simple 8-byte ints. 
158
+The following benchmarks were run on my 2019 Macbook Pro (2.4 GHz 8-Core Intel Core i9) using Go 1.16.5. The items are simple 8-byte ints. 
159 159
 
160 160
 - `google`: The [google/btree](https://github.com/google/btree) package
161 161
 - `tidwall`: The [tidwall/btree](https://github.com/tidwall/btree) package
@@ -163,29 +163,29 @@ The following benchmarks were run on my 2019 Macbook Pro (2.4 GHz 8-Core Intel C
163 163
 
164 164
 ```
165 165
 ** sequential set **
166
-google:  set-seq        1,000,000 ops in 160ms, 6,262,097/sec, 159 ns/op, 31.0 MB, 32 bytes/op
167
-tidwall: set-seq        1,000,000 ops in 142ms, 7,020,721/sec, 142 ns/op, 36.6 MB, 38 bytes/op
168
-tidwall: set-seq-hint   1,000,000 ops in 87ms, 11,503,315/sec, 86 ns/op, 36.6 MB, 38 bytes/op
169
-tidwall: load-seq       1,000,000 ops in 37ms, 27,177,242/sec, 36 ns/op, 36.6 MB, 38 bytes/op
170
-go-arr:  append         1,000,000 ops in 49ms, 20,574,760/sec, 48 ns/op
166
+google:  set-seq        1,000,000 ops in 163ms, 6,140,597/sec, 162 ns/op, 30.9 MB, 32 bytes/op
167
+tidwall: set-seq        1,000,000 ops in 141ms, 7,075,240/sec, 141 ns/op, 36.6 MB, 38 bytes/op
168
+tidwall: set-seq-hint   1,000,000 ops in 79ms, 12,673,902/sec, 78 ns/op, 36.6 MB, 38 bytes/op
169
+tidwall: load-seq       1,000,000 ops in 40ms, 24,887,293/sec, 40 ns/op, 36.6 MB, 38 bytes/op
170
+go-arr:  append         1,000,000 ops in 51ms, 19,617,269/sec, 50 ns/op
171 171
 
172 172
 ** random set **
173
-google:  set-rand       1,000,000 ops in 606ms, 1,649,921/sec, 606 ns/op, 21.5 MB, 22 bytes/op
174
-tidwall: set-rand       1,000,000 ops in 543ms, 1,841,590/sec, 543 ns/op, 26.7 MB, 27 bytes/op
175
-tidwall: set-rand-hint  1,000,000 ops in 573ms, 1,745,624/sec, 572 ns/op, 26.4 MB, 27 bytes/op
176
-tidwall: set-again      1,000,000 ops in 452ms, 2,212,581/sec, 451 ns/op, 27.1 MB, 28 bytes/op
177
-tidwall: set-after-copy 1,000,000 ops in 472ms, 2,117,457/sec, 472 ns/op, 27.9 MB, 29 bytes/op
178
-tidwall: load-rand      1,000,000 ops in 551ms, 1,816,498/sec, 550 ns/op, 26.1 MB, 27 bytes/op
173
+google:  set-rand       1,000,000 ops in 666ms, 1,501,583/sec, 665 ns/op, 21.5 MB, 22 bytes/op
174
+tidwall: set-rand       1,000,000 ops in 569ms, 1,756,845/sec, 569 ns/op, 26.7 MB, 27 bytes/op
175
+tidwall: set-rand-hint  1,000,000 ops in 670ms, 1,491,637/sec, 670 ns/op, 26.4 MB, 27 bytes/op
176
+tidwall: set-again      1,000,000 ops in 488ms, 2,050,667/sec, 487 ns/op, 27.1 MB, 28 bytes/op
177
+tidwall: set-after-copy 1,000,000 ops in 494ms, 2,022,980/sec, 494 ns/op, 27.9 MB, 29 bytes/op
178
+tidwall: load-rand      1,000,000 ops in 594ms, 1,682,937/sec, 594 ns/op, 26.1 MB, 27 bytes/op
179 179
 
180 180
 ** sequential get **
181
-google:  get-seq        1,000,000 ops in 133ms, 7,497,604/sec, 133 ns/op
182
-tidwall: get-seq        1,000,000 ops in 110ms, 9,082,972/sec, 110 ns/op
183
-tidwall: get-seq-hint   1,000,000 ops in 55ms, 18,289,945/sec, 54 ns/op
181
+google:  get-seq        1,000,000 ops in 141ms, 7,078,690/sec, 141 ns/op
182
+tidwall: get-seq        1,000,000 ops in 124ms, 8,075,925/sec, 123 ns/op
183
+tidwall: get-seq-hint   1,000,000 ops in 40ms, 25,142,979/sec, 39 ns/op
184 184
 
185 185
 ** random get **
186
-google:  get-rand       1,000,000 ops in 149ms, 6,704,337/sec, 149 ns/op
187
-tidwall: get-rand       1,000,000 ops in 131ms, 7,616,296/sec, 131 ns/op
188
-tidwall: get-rand-hint  1,000,000 ops in 216ms, 4,632,532/sec, 215 ns/op
186
+google:  get-rand       1,000,000 ops in 152ms, 6,593,518/sec, 151 ns/op
187
+tidwall: get-rand       1,000,000 ops in 128ms, 7,783,293/sec, 128 ns/op
188
+tidwall: get-rand-hint  1,000,000 ops in 135ms, 7,403,823/sec, 135 ns/op
189 189
 ```
190 190
 
191 191
 *You can find the benchmark utility at [tidwall/btree-benchmark](https://github.com/tidwall/btree-benchmark)*

+ 303
- 89
vendor/github.com/tidwall/btree/btree.go View File

@@ -4,9 +4,11 @@
4 4
 
5 5
 package btree
6 6
 
7
-import "sync"
7
+import (
8
+	"sync"
9
+)
8 10
 
9
-const maxItems = 255
11
+const maxItems = 255 // max items per node. max children is +1
10 12
 const minItems = maxItems * 40 / 100
11 13
 
12 14
 type cow struct {
@@ -17,18 +19,19 @@ type node struct {
17 19
 	cow      *cow
18 20
 	leaf     bool
19 21
 	numItems int16
22
+	count    int
20 23
 	items    [maxItems]interface{}
21 24
 	children *[maxItems + 1]*node
22 25
 }
23 26
 
24 27
 // BTree is an ordered set items
25 28
 type BTree struct {
26
-	mu     *sync.RWMutex
27
-	cow    *cow
28
-	root   *node
29
-	length int
30
-	less   func(a, b interface{}) bool
31
-	lnode  *node
29
+	mu    *sync.RWMutex
30
+	cow   *cow
31
+	root  *node
32
+	count int
33
+	less  func(a, b interface{}) bool
34
+	locks bool
32 35
 }
33 36
 
34 37
 func (tr *BTree) newNode(leaf bool) *node {
@@ -43,17 +46,32 @@ func (tr *BTree) newNode(leaf bool) *node {
43 46
 // PathHint is a utility type used with the *Hint() functions. Hints provide
44 47
 // faster operations for clustered keys.
45 48
 type PathHint struct {
49
+	used [8]bool
46 50
 	path [8]uint8
47 51
 }
48 52
 
49 53
 // New returns a new BTree
50 54
 func New(less func(a, b interface{}) bool) *BTree {
55
+	return newBTree(less, true)
56
+}
57
+
58
+// NewNonConcurrent returns a new BTree which is not safe for concurrent
59
+// write operations by multiple goroutines.
60
+//
61
+// This is useful for when you do not need the BTree to manage the locking,
62
+// but would rather do it yourself.
63
+func NewNonConcurrent(less func(a, b interface{}) bool) *BTree {
64
+	return newBTree(less, false)
65
+}
66
+
67
+func newBTree(less func(a, b interface{}) bool, locks bool) *BTree {
51 68
 	if less == nil {
52 69
 		panic("nil less")
53 70
 	}
54 71
 	tr := new(BTree)
55 72
 	tr.mu = new(sync.RWMutex)
56 73
 	tr.less = less
74
+	tr.locks = locks
57 75
 	return tr
58 76
 }
59 77
 
@@ -68,18 +86,32 @@ func (n *node) find(key interface{}, less func(a, b interface{}) bool,
68 86
 ) (index int16, found bool) {
69 87
 	low := int16(0)
70 88
 	high := n.numItems - 1
71
-	if hint != nil && depth < 8 {
89
+	if hint != nil && depth < 8 && hint.used[depth] {
72 90
 		index = int16(hint.path[depth])
73
-		if index > n.numItems-1 {
91
+		if index >= n.numItems {
92
+			// tail item
93
+			if less(n.items[n.numItems-1], key) {
94
+				if less(key, n.items[n.numItems-1]) {
95
+					index = n.numItems - 1
96
+					found = true
97
+					goto path_match
98
+				} else {
99
+					index = n.numItems
100
+					goto path_match
101
+				}
102
+			}
74 103
 			index = n.numItems - 1
75 104
 		}
76 105
 		if less(key, n.items[index]) {
106
+			if index == 0 || less(n.items[index-1], key) {
107
+				goto path_match
108
+			}
77 109
 			high = index - 1
78 110
 		} else if less(n.items[index], key) {
79 111
 			low = index + 1
80 112
 		} else {
81 113
 			found = true
82
-			goto done
114
+			goto path_match
83 115
 		}
84 116
 	}
85 117
 	for low <= high {
@@ -97,13 +129,16 @@ func (n *node) find(key interface{}, less func(a, b interface{}) bool,
97 129
 		index = low
98 130
 		found = false
99 131
 	}
100
-done:
101
-	if hint != nil && depth < 8 {
102
-		if n.leaf && found {
103
-			hint.path[depth] = byte(index + 1)
104
-		} else {
105
-			hint.path[depth] = byte(index)
106
-		}
132
+	if hint == nil || depth >= 8 {
133
+		return index, found
134
+	}
135
+
136
+path_match:
137
+	hint.used[depth] = true
138
+	if n.leaf && found {
139
+		hint.path[depth] = byte(index + 1)
140
+	} else {
141
+		hint.path[depth] = byte(index)
107 142
 	}
108 143
 	return index, found
109 144
 }
@@ -113,10 +148,10 @@ func (tr *BTree) SetHint(item interface{}, hint *PathHint) (prev interface{}) {
113 148
 	if item == nil {
114 149
 		panic("nil item")
115 150
 	}
116
-	tr.mu.Lock()
117
-	prev = tr.setHint(item, hint)
118
-	tr.mu.Unlock()
119
-	return prev
151
+	if tr.lock() {
152
+		defer tr.unlock()
153
+	}
154
+	return tr.setHint(item, hint)
120 155
 }
121 156
 
122 157
 func (tr *BTree) setHint(item interface{}, hint *PathHint) (prev interface{}) {
@@ -124,14 +159,14 @@ func (tr *BTree) setHint(item interface{}, hint *PathHint) (prev interface{}) {
124 159
 		tr.root = tr.newNode(true)
125 160
 		tr.root.items[0] = item
126 161
 		tr.root.numItems = 1
127
-		tr.length = 1
162
+		tr.root.count = 1
163
+		tr.count = 1
128 164
 		return
129 165
 	}
130 166
 	prev = tr.nodeSet(&tr.root, item, tr.less, hint, 0)
131 167
 	if prev != nil {
132 168
 		return prev
133 169
 	}
134
-	tr.lnode = nil
135 170
 	if tr.root.numItems == maxItems {
136 171
 		n := tr.cowLoad(&tr.root)
137 172
 		right, median := tr.nodeSplit(n)
@@ -140,8 +175,9 @@ func (tr *BTree) setHint(item interface{}, hint *PathHint) (prev interface{}) {
140 175
 		tr.root.items[0] = median
141 176
 		tr.root.children[1] = right
142 177
 		tr.root.numItems = 1
178
+		tr.root.count = n.count + 1 + right.count
143 179
 	}
144
-	tr.length++
180
+	tr.count++
145 181
 	return prev
146 182
 }
147 183
 
@@ -167,10 +203,25 @@ func (tr *BTree) nodeSplit(n *node) (right *node, median interface{}) {
167 203
 		n.items[i] = nil
168 204
 	}
169 205
 	n.numItems = maxItems / 2
206
+	// update counts
207
+	n.updateCount()
208
+	right.updateCount()
170 209
 	return right, median
171 210
 }
172 211
 
173
-//go:noinline
212
+func (n *node) updateCount() {
213
+	n.count = int(n.numItems)
214
+	if !n.leaf {
215
+		for i := 0; i <= int(n.numItems); i++ {
216
+			n.count += n.children[i].count
217
+		}
218
+	}
219
+}
220
+
221
+// This operation should not be inlined because it's expensive and rarely
222
+// called outside of heavy copy-on-write situations. Marking it "noinline"
223
+// allows for the parent cowLoad to be inlined.
224
+// go:noinline
174 225
 func (tr *BTree) copy(n *node) *node {
175 226
 	n2 := *n
176 227
 	n2.cow = tr.cow
@@ -182,7 +233,7 @@ func (tr *BTree) copy(n *node) *node {
182 233
 	return &n2
183 234
 }
184 235
 
185
-// cowLoad loaded the provide node and, if needed, performs a copy-on-write.
236
+// cowLoad loads the provide node and, if needed, performs a copy-on-write.
186 237
 func (tr *BTree) cowLoad(cn **node) *node {
187 238
 	if (*cn).cow != tr.cow {
188 239
 		*cn = tr.copy(*cn)
@@ -204,6 +255,7 @@ func (tr *BTree) nodeSet(cn **node, item interface{},
204 255
 		copy(n.items[i+1:n.numItems+1], n.items[i:n.numItems])
205 256
 		n.items[i] = item
206 257
 		n.numItems++
258
+		n.count++
207 259
 		return nil
208 260
 	}
209 261
 	prev = tr.nodeSet(&n.children[i], item, less, hint, depth+1)
@@ -218,7 +270,8 @@ func (tr *BTree) nodeSet(cn **node, item interface{},
218 270
 		n.children[i+1] = right
219 271
 		n.numItems++
220 272
 	}
221
-	return prev
273
+	n.count++
274
+	return nil
222 275
 }
223 276
 
224 277
 func (n *node) scan(iter func(item interface{}) bool) bool {
@@ -248,8 +301,9 @@ func (tr *BTree) Get(key interface{}) interface{} {
248 301
 
249 302
 // GetHint gets a value for key using a path hint
250 303
 func (tr *BTree) GetHint(key interface{}, hint *PathHint) interface{} {
251
-	tr.mu.RLock()
252
-	defer tr.mu.RUnlock()
304
+	if tr.rlock() {
305
+		defer tr.runlock()
306
+	}
253 307
 	if tr.root == nil || key == nil {
254 308
 		return nil
255 309
 	}
@@ -270,7 +324,7 @@ func (tr *BTree) GetHint(key interface{}, hint *PathHint) interface{} {
270 324
 
271 325
 // Len returns the number of items in the tree
272 326
 func (tr *BTree) Len() int {
273
-	return tr.length
327
+	return tr.count
274 328
 }
275 329
 
276 330
 // Delete a value for a key
@@ -280,10 +334,10 @@ func (tr *BTree) Delete(key interface{}) interface{} {
280 334
 
281 335
 // DeleteHint deletes a value for a key using a path hint
282 336
 func (tr *BTree) DeleteHint(key interface{}, hint *PathHint) interface{} {
283
-	tr.mu.Lock()
284
-	prev := tr.deleteHint(key, hint)
285
-	tr.mu.Unlock()
286
-	return prev
337
+	if tr.lock() {
338
+		defer tr.unlock()
339
+	}
340
+	return tr.deleteHint(key, hint)
287 341
 }
288 342
 
289 343
 func (tr *BTree) deleteHint(key interface{}, hint *PathHint) interface{} {
@@ -294,12 +348,11 @@ func (tr *BTree) deleteHint(key interface{}, hint *PathHint) interface{} {
294 348
 	if prev == nil {
295 349
 		return nil
296 350
 	}
297
-	tr.lnode = nil
298 351
 	if tr.root.numItems == 0 && !tr.root.leaf {
299 352
 		tr.root = tr.root.children[0]
300 353
 	}
301
-	tr.length--
302
-	if tr.length == 0 {
354
+	tr.count--
355
+	if tr.count == 0 {
303 356
 		tr.root = nil
304 357
 	}
305 358
 	return prev
@@ -323,6 +376,7 @@ func (tr *BTree) delete(cn **node, max bool, key interface{},
323 376
 			copy(n.items[i:], n.items[i+1:n.numItems])
324 377
 			n.items[n.numItems-1] = nil
325 378
 			n.numItems--
379
+			n.count--
326 380
 			return prev
327 381
 		}
328 382
 		return nil
@@ -344,6 +398,7 @@ func (tr *BTree) delete(cn **node, max bool, key interface{},
344 398
 	if prev == nil {
345 399
 		return nil
346 400
 	}
401
+	n.count--
347 402
 	if n.children[i].numItems >= minItems {
348 403
 		return prev
349 404
 	}
@@ -364,10 +419,11 @@ func (tr *BTree) delete(cn **node, max bool, key interface{},
364 419
 				n.children[i+1].children[:n.children[i+1].numItems+1])
365 420
 		}
366 421
 		n.children[i].numItems += n.children[i+1].numItems + 1
422
+		n.children[i].count += n.children[i+1].count + 1
367 423
 		copy(n.items[i:], n.items[i+1:n.numItems])
368 424
 		copy(n.children[i+1:], n.children[i+2:n.numItems+1])
369
-		n.items[n.numItems] = nil
370
-		n.children[n.numItems+1] = nil
425
+		n.items[n.numItems-1] = nil
426
+		n.children[n.numItems] = nil
371 427
 		n.numItems--
372 428
 	} else if n.children[i].numItems > n.children[i+1].numItems {
373 429
 		// move left -> right
@@ -381,30 +437,42 @@ func (tr *BTree) delete(cn **node, max bool, key interface{},
381 437
 		if !n.children[0].leaf {
382 438
 			n.children[i+1].children[0] =
383 439
 				n.children[i].children[n.children[i].numItems]
440
+			n.children[i+1].count += n.children[i+1].children[0].count
384 441
 		}
385 442
 		n.children[i+1].numItems++
443
+		n.children[i+1].count++
386 444
 		n.items[i] = n.children[i].items[n.children[i].numItems-1]
387 445
 		n.children[i].items[n.children[i].numItems-1] = nil
388 446
 		if !n.children[0].leaf {
389 447
 			n.children[i].children[n.children[i].numItems] = nil
448
+			n.children[i].count -= n.children[i+1].children[0].count
390 449
 		}
391 450
 		n.children[i].numItems--
451
+		n.children[i].count--
392 452
 	} else {
393
-		// move right -> left
453
+		// move left <- right
394 454
 		n.children[i].items[n.children[i].numItems] = n.items[i]
395 455
 		if !n.children[0].leaf {
396 456
 			n.children[i].children[n.children[i].numItems+1] =
397 457
 				n.children[i+1].children[0]
458
+			n.children[i].count +=
459
+				n.children[i].children[n.children[i].numItems+1].count
398 460
 		}
399 461
 		n.children[i].numItems++
462
+		n.children[i].count++
400 463
 		n.items[i] = n.children[i+1].items[0]
401 464
 		copy(n.children[i+1].items[:],
402 465
 			n.children[i+1].items[1:n.children[i+1].numItems])
466
+		n.children[i+1].items[n.children[i+1].numItems-1] = nil
403 467
 		if !n.children[0].leaf {
404 468
 			copy(n.children[i+1].children[:],
405 469
 				n.children[i+1].children[1:n.children[i+1].numItems+1])
470
+			n.children[i+1].children[n.children[i+1].numItems] = nil
471
+			n.children[i+1].count -=
472
+				n.children[i].children[n.children[i].numItems].count
406 473
 		}
407 474
 		n.children[i+1].numItems--
475
+		n.children[i+1].count--
408 476
 	}
409 477
 	return prev
410 478
 }
@@ -413,8 +481,9 @@ func (tr *BTree) delete(cn **node, max bool, key interface{},
413 481
 // Pass nil for pivot to scan all item in ascending order
414 482
 // Return false to stop iterating
415 483
 func (tr *BTree) Ascend(pivot interface{}, iter func(item interface{}) bool) {
416
-	tr.mu.RLock()
417
-	defer tr.mu.RUnlock()
484
+	if tr.rlock() {
485
+		defer tr.runlock()
486
+	}
418 487
 	if tr.root == nil {
419 488
 		return
420 489
 	}
@@ -476,8 +545,9 @@ func (n *node) reverse(iter func(item interface{}) bool) bool {
476 545
 // Pass nil for pivot to scan all item in descending order
477 546
 // Return false to stop iterating
478 547
 func (tr *BTree) Descend(pivot interface{}, iter func(item interface{}) bool) {
479
-	tr.mu.RLock()
480
-	defer tr.mu.RUnlock()
548
+	if tr.rlock() {
549
+		defer tr.runlock()
550
+	}
481 551
 	if tr.root == nil {
482 552
 		return
483 553
 	}
@@ -518,40 +588,46 @@ func (tr *BTree) Load(item interface{}) interface{} {
518 588
 	if item == nil {
519 589
 		panic("nil item")
520 590
 	}
521
-	tr.mu.Lock()
522
-	defer tr.mu.Unlock()
523
-
524
-	// Load does not need a cowGrid because the Copy operation sets the
525
-	// lnode to nil.
526
-
527
-	if tr.lnode != nil && tr.lnode.numItems < maxItems-2 {
528
-		if tr.less(tr.lnode.items[tr.lnode.numItems-1], item) {
529
-			tr.lnode.items[tr.lnode.numItems] = item
530
-			tr.lnode.numItems++
531
-			tr.length++
532
-			return nil
533
-		}
591
+	if tr.lock() {
592
+		defer tr.unlock()
534 593
 	}
535
-	prev := tr.setHint(item, nil)
536
-	if prev != nil {
537
-		return prev
594
+	if tr.root == nil {
595
+		return tr.setHint(item, nil)
538 596
 	}
539
-	n := tr.root
597
+	n := tr.cowLoad(&tr.root)
540 598
 	for {
599
+		n.count++ // optimistically update counts
600
+		if n.leaf {
601
+			if n.numItems < maxItems-2 {
602
+				if tr.less(n.items[n.numItems-1], item) {
603
+					n.items[n.numItems] = item
604
+					n.numItems++
605
+					tr.count++
606
+					return nil
607
+				}
608
+			}
609
+			break
610
+		}
611
+		n = tr.cowLoad(&n.children[n.numItems])
612
+	}
613
+	// revert the counts
614
+	n = tr.root
615
+	for {
616
+		n.count--
541 617
 		if n.leaf {
542
-			tr.lnode = n
543 618
 			break
544 619
 		}
545 620
 		n = n.children[n.numItems]
546 621
 	}
547
-	return nil
622
+	return tr.setHint(item, nil)
548 623
 }
549 624
 
550 625
 // Min returns the minimum item in tree.
551 626
 // Returns nil if the tree has no items.
552 627
 func (tr *BTree) Min() interface{} {
553
-	tr.mu.RLock()
554
-	defer tr.mu.RUnlock()
628
+	if tr.rlock() {
629
+		defer tr.runlock()
630
+	}
555 631
 	if tr.root == nil {
556 632
 		return nil
557 633
 	}
@@ -567,8 +643,9 @@ func (tr *BTree) Min() interface{} {
567 643
 // Max returns the maximum item in tree.
568 644
 // Returns nil if the tree has no items.
569 645
 func (tr *BTree) Max() interface{} {
570
-	tr.mu.RLock()
571
-	defer tr.mu.RUnlock()
646
+	if tr.rlock() {
647
+		defer tr.runlock()
648
+	}
572 649
 	if tr.root == nil {
573 650
 		return nil
574 651
 	}
@@ -584,65 +661,179 @@ func (tr *BTree) Max() interface{} {
584 661
 // PopMin removes the minimum item in tree and returns it.
585 662
 // Returns nil if the tree has no items.
586 663
 func (tr *BTree) PopMin() interface{} {
587
-	tr.mu.Lock()
588
-	defer tr.mu.Unlock()
664
+	if tr.lock() {
665
+		defer tr.unlock()
666
+	}
589 667
 	if tr.root == nil {
590 668
 		return nil
591 669
 	}
592
-	tr.lnode = nil
593 670
 	n := tr.cowLoad(&tr.root)
671
+	var item interface{}
594 672
 	for {
673
+		n.count-- // optimistically update counts
595 674
 		if n.leaf {
596
-			item := n.items[0]
675
+			item = n.items[0]
597 676
 			if n.numItems == minItems {
598
-				return tr.deleteHint(item, nil)
677
+				break
599 678
 			}
600 679
 			copy(n.items[:], n.items[1:])
601 680
 			n.items[n.numItems-1] = nil
602 681
 			n.numItems--
603
-			tr.length--
604
-			if tr.length == 0 {
682
+			tr.count--
683
+			if tr.count == 0 {
605 684
 				tr.root = nil
606 685
 			}
607 686
 			return item
608 687
 		}
609 688
 		n = tr.cowLoad(&n.children[0])
610 689
 	}
690
+	// revert the counts
691
+	n = tr.root
692
+	for {
693
+		n.count++
694
+		if n.leaf {
695
+			break
696
+		}
697
+		n = n.children[0]
698
+	}
699
+	return tr.deleteHint(item, nil)
611 700
 }
612 701
 
613 702
 // PopMax removes the minimum item in tree and returns it.
614 703
 // Returns nil if the tree has no items.
615 704
 func (tr *BTree) PopMax() interface{} {
616
-	tr.mu.Lock()
617
-	defer tr.mu.Unlock()
705
+	if tr.lock() {
706
+		defer tr.unlock()
707
+	}
618 708
 	if tr.root == nil {
619 709
 		return nil
620 710
 	}
621
-	tr.lnode = nil
622 711
 	n := tr.cowLoad(&tr.root)
712
+	var item interface{}
623 713
 	for {
714
+		n.count-- // optimistically update counts
624 715
 		if n.leaf {
625
-			item := n.items[n.numItems-1]
716
+			item = n.items[n.numItems-1]
626 717
 			if n.numItems == minItems {
627
-				return tr.deleteHint(item, nil)
718
+				break
628 719
 			}
629 720
 			n.items[n.numItems-1] = nil
630 721
 			n.numItems--
631
-			tr.length--
632
-			if tr.length == 0 {
722
+			tr.count--
723
+			if tr.count == 0 {
633 724
 				tr.root = nil
634 725
 			}
635 726
 			return item
636 727
 		}
637 728
 		n = tr.cowLoad(&n.children[n.numItems])
638 729
 	}
730
+	// revert the counts
731
+	n = tr.root
732
+	for {
733
+		n.count++
734
+		if n.leaf {
735
+			break
736
+		}
737
+		n = n.children[n.numItems]
738
+	}
739
+	return tr.deleteHint(item, nil)
740
+}
741
+
742
+// GetAt returns the value at index.
743
+// Return nil if the tree is empty or the index is out of bounds.
744
+func (tr *BTree) GetAt(index int) interface{} {
745
+	if tr.rlock() {
746
+		defer tr.runlock()
747
+	}
748
+	if tr.root == nil || index < 0 || index >= tr.count {
749
+		return nil
750
+	}
751
+	n := tr.root
752
+	for {
753
+		if n.leaf {
754
+			return n.items[index]
755
+		}
756
+		i := 0
757
+		for ; i < int(n.numItems); i++ {
758
+			if index < n.children[i].count {
759
+				break
760
+			} else if index == n.children[i].count {
761
+				return n.items[i]
762
+			}
763
+			index -= n.children[i].count + 1
764
+		}
765
+		n = n.children[i]
766
+	}
767
+}
768
+
769
+// DeleteAt deletes the item at index.
770
+// Return nil if the tree is empty or the index is out of bounds.
771
+func (tr *BTree) DeleteAt(index int) interface{} {
772
+	if tr.lock() {
773
+		defer tr.unlock()
774
+	}
775
+	if tr.root == nil || index < 0 || index >= tr.count {
776
+		return nil
777
+	}
778
+	var pathbuf [8]uint8 // track the path
779
+	path := pathbuf[:0]
780
+	var item interface{}
781
+	n := tr.cowLoad(&tr.root)
782
+outer:
783
+	for {
784
+		n.count-- // optimistically update counts
785
+		if n.leaf {
786
+			// the index is the item position
787
+			item = n.items[index]
788
+			if n.numItems == minItems {
789
+				path = append(path, uint8(index))
790
+				break outer
791
+			}
792
+			copy(n.items[index:], n.items[index+1:n.numItems])
793
+			n.items[n.numItems-1] = nil
794
+			n.numItems--
795
+			tr.count--
796
+			if tr.count == 0 {
797
+				tr.root = nil
798
+			}
799
+			return item
800
+		}
801
+		i := 0
802
+		for ; i < int(n.numItems); i++ {
803
+			if index < n.children[i].count {
804
+				break
805
+			} else if index == n.children[i].count {
806
+				item = n.items[i]
807
+				path = append(path, uint8(i))
808
+				break outer
809
+			}
810
+			index -= n.children[i].count + 1
811
+		}
812
+		path = append(path, uint8(i))
813
+		n = tr.cowLoad(&n.children[i])
814
+	}
815
+	// revert the counts
816
+	var hint PathHint
817
+	n = tr.root
818
+	for i := 0; i < len(path); i++ {
819
+		if i < len(hint.path) {
820
+			hint.path[i] = path[i]
821
+			hint.used[i] = true
822
+		}
823
+		n.count++
824
+		if !n.leaf {
825
+			n = n.children[uint8(path[i])]
826
+		}
827
+	}
828
+	return tr.deleteHint(item, &hint)
639 829
 }
640 830
 
641 831
 // Height returns the height of the tree.
642 832
 // Returns zero if tree has no items.
643 833
 func (tr *BTree) Height() int {
644
-	tr.mu.RLock()
645
-	defer tr.mu.RUnlock()
834
+	if tr.rlock() {
835
+		defer tr.runlock()
836
+	}
646 837
 	var height int
647 838
 	if tr.root != nil {
648 839
 		n := tr.root
@@ -660,8 +851,9 @@ func (tr *BTree) Height() int {
660 851
 // Walk iterates over all items in tree, in order.
661 852
 // The items param will contain one or more items.
662 853
 func (tr *BTree) Walk(iter func(item []interface{})) {
663
-	tr.mu.RLock()
664
-	defer tr.mu.RUnlock()
854
+	if tr.rlock() {
855
+		defer tr.runlock()
856
+	}
665 857
 	if tr.root != nil {
666 858
 		tr.root.walk(iter)
667 859
 	}
@@ -682,12 +874,34 @@ func (n *node) walk(iter func(item []interface{})) {
682 874
 // Copy the tree. This operation is very fast because it only performs a
683 875
 // shadowed copy.
684 876
 func (tr *BTree) Copy() *BTree {
685
-	tr.mu.Lock()
686
-	tr.lnode = nil
877
+	if tr.lock() {
878
+		defer tr.unlock()
879
+	}
687 880
 	tr.cow = new(cow)
688 881
 	tr2 := *tr
689 882
 	tr2.mu = new(sync.RWMutex)
690 883
 	tr2.cow = new(cow)
691
-	tr.mu.Unlock()
692 884
 	return &tr2
693 885
 }
886
+
887
+func (tr *BTree) lock() bool {
888
+	if tr.locks {
889
+		tr.mu.Lock()
890
+	}
891
+	return tr.locks
892
+}
893
+
894
+func (tr *BTree) unlock() {
895
+	tr.mu.Unlock()
896
+}
897
+
898
+func (tr *BTree) rlock() bool {
899
+	if tr.locks {
900
+		tr.mu.RLock()
901
+	}
902
+	return tr.locks
903
+}
904
+
905
+func (tr *BTree) runlock() {
906
+	tr.mu.RUnlock()
907
+}

+ 3
- 0
vendor/github.com/tidwall/btree/go.mod View File

@@ -0,0 +1,3 @@
1
+module github.com/tidwall/btree
2
+
3
+go 1.16

+ 3
- 4
vendor/github.com/tidwall/buntdb/README.md View File

@@ -3,7 +3,6 @@
3 3
     src="logo.png"
4 4
     width="307" height="150" border="0" alt="BuntDB">
5 5
 <br>
6
-<a href="https://goreportcard.com/report/github.com/tidwall/buntdb"><img src="https://goreportcard.com/badge/github.com/tidwall/buntdb?style=flat-square" alt="Go Report Card"></a>
7 6
 <a href="https://godoc.org/github.com/tidwall/buntdb"><img src="https://img.shields.io/badge/go-documentation-blue.svg?style=flat-square" alt="Godoc"></a>
8 7
 <a href="https://github.com/tidwall/buntdb/blob/master/LICENSE"><img src="https://img.shields.io/github/license/tidwall/buntdb.svg?style=flat-square" alt="LICENSE"></a>
9 8
 </p>
@@ -28,7 +27,6 @@ Features
28 27
 - Flexible [iteration](#iterating) of data; ascending, descending, and ranges
29 28
 - [Durable append-only file](#append-only-file) format for persistence
30 29
 - Option to evict old items with an [expiration](#data-expiration) TTL
31
-- Tight codebase, under 2K loc using the `cloc` command
32 30
 - ACID semantics with locking [transactions](#transactions) that support rollbacks
33 31
 
34 32
 
@@ -457,8 +455,9 @@ Any index can be put in descending order by wrapping it's less function with `bu
457 455
 
458 456
 ```go
459 457
 db.CreateIndex("last_name_age", "*",
460
-buntdb.IndexJSON("name.last"),
461
-buntdb.Desc(buntdb.IndexJSON("age")))
458
+    buntdb.IndexJSON("name.last"),
459
+    buntdb.Desc(buntdb.IndexJSON("age")),
460
+)
462 461
 ```
463 462
 
464 463
 This will create a multi value index where the last name is ascending and the age is descending.

+ 86
- 24
vendor/github.com/tidwall/buntdb/buntdb.go View File

@@ -69,6 +69,7 @@ type DB struct {
69 69
 	keys      *btree.BTree      // a tree of all item ordered by key
70 70
 	exps      *btree.BTree      // a tree of items ordered by expiration
71 71
 	idxs      map[string]*index // the index trees.
72
+	insIdxs   []*index          // a reuse buffer for gathering indexes
72 73
 	flushes   int               // a count of the number of disk flushes
73 74
 	closed    bool              // set when the database has been closed
74 75
 	config    Config            // the database configuration
@@ -139,8 +140,8 @@ type exctx struct {
139 140
 func Open(path string) (*DB, error) {
140 141
 	db := &DB{}
141 142
 	// initialize trees and indexes
142
-	db.keys = btree.New(lessCtx(nil))
143
-	db.exps = btree.New(lessCtx(&exctx{db}))
143
+	db.keys = btreeNew(lessCtx(nil))
144
+	db.exps = btreeNew(lessCtx(&exctx{db}))
144 145
 	db.idxs = make(map[string]*index)
145 146
 	// initialize default configuration
146 147
 	db.config = Config{
@@ -200,10 +201,11 @@ func (db *DB) Save(wr io.Writer) error {
200 201
 	defer db.mu.RUnlock()
201 202
 	// use a buffered writer and flush every 4MB
202 203
 	var buf []byte
204
+	now := time.Now()
203 205
 	// iterated through every item in the database and write to the buffer
204 206
 	btreeAscend(db.keys, func(item interface{}) bool {
205 207
 		dbi := item.(*dbItem)
206
-		buf = dbi.writeSetTo(buf)
208
+		buf = dbi.writeSetTo(buf, now)
207 209
 		if len(buf) > 1024*1024*4 {
208 210
 			// flush when buffer is over 4MB
209 211
 			_, err = wr.Write(buf)
@@ -283,7 +285,7 @@ func (idx *index) clearCopy() *index {
283 285
 	}
284 286
 	// initialize with empty trees
285 287
 	if nidx.less != nil {
286
-		nidx.btr = btree.New(lessCtx(nidx))
288
+		nidx.btr = btreeNew(lessCtx(nidx))
287 289
 	}
288 290
 	if nidx.rect != nil {
289 291
 		nidx.rtr = rtred.New(nidx)
@@ -295,7 +297,7 @@ func (idx *index) clearCopy() *index {
295 297
 func (idx *index) rebuild() {
296 298
 	// initialize trees
297 299
 	if idx.less != nil {
298
-		idx.btr = btree.New(lessCtx(idx))
300
+		idx.btr = btreeNew(lessCtx(idx))
299 301
 	}
300 302
 	if idx.rect != nil {
301 303
 		idx.rtr = rtred.New(idx)
@@ -454,16 +456,23 @@ func (db *DB) SetConfig(config Config) error {
454 456
 // will be replaced with the new one, and return the previous item.
455 457
 func (db *DB) insertIntoDatabase(item *dbItem) *dbItem {
456 458
 	var pdbi *dbItem
459
+	// Generate a list of indexes that this item will be inserted in to.
460
+	idxs := db.insIdxs
461
+	for _, idx := range db.idxs {
462
+		if idx.match(item.key) {
463
+			idxs = append(idxs, idx)
464
+		}
465
+	}
457 466
 	prev := db.keys.Set(item)
458 467
 	if prev != nil {
459 468
 		// A previous item was removed from the keys tree. Let's
460 469
 		// fully delete this item from all indexes.
461 470
 		pdbi = prev.(*dbItem)
462 471
 		if pdbi.opts != nil && pdbi.opts.ex {
463
-			// Remove it from the exipres tree.
472
+			// Remove it from the expires tree.
464 473
 			db.exps.Delete(pdbi)
465 474
 		}
466
-		for _, idx := range db.idxs {
475
+		for _, idx := range idxs {
467 476
 			if idx.btr != nil {
468 477
 				// Remove it from the btree index.
469 478
 				idx.btr.Delete(pdbi)
@@ -479,10 +488,7 @@ func (db *DB) insertIntoDatabase(item *dbItem) *dbItem {
479 488
 		// expires tree
480 489
 		db.exps.Set(item)
481 490
 	}
482
-	for _, idx := range db.idxs {
483
-		if !idx.match(item.key) {
484
-			continue
485
-		}
491
+	for i, idx := range idxs {
486 492
 		if idx.btr != nil {
487 493
 			// Add new item to btree index.
488 494
 			idx.btr.Set(item)
@@ -491,7 +497,11 @@ func (db *DB) insertIntoDatabase(item *dbItem) *dbItem {
491 497
 			// Add new item to rtree index.
492 498
 			idx.rtr.Insert(item)
493 499
 		}
500
+		// clear the index
501
+		idxs[i] = nil
494 502
 	}
503
+	// reuse the index list slice
504
+	db.insIdxs = idxs[:0]
495 505
 	// we must return the previous item to the caller.
496 506
 	return pdbi
497 507
 }
@@ -512,6 +522,9 @@ func (db *DB) deleteFromDatabase(item *dbItem) *dbItem {
512 522
 			db.exps.Delete(pdbi)
513 523
 		}
514 524
 		for _, idx := range db.idxs {
525
+			if !idx.match(pdbi.key) {
526
+				continue
527
+			}
515 528
 			if idx.btr != nil {
516 529
 				// Remove it from the btree index.
517 530
 				idx.btr.Delete(pdbi)
@@ -672,6 +685,7 @@ func (db *DB) Shrink() error {
672 685
 			}
673 686
 			done = true
674 687
 			var n int
688
+			now := time.Now()
675 689
 			btreeAscendGreaterOrEqual(db.keys, &dbItem{key: pivot},
676 690
 				func(item interface{}) bool {
677 691
 					dbi := item.(*dbItem)
@@ -681,7 +695,7 @@ func (db *DB) Shrink() error {
681 695
 						done = false
682 696
 						return false
683 697
 					}
684
-					buf = dbi.writeSetTo(buf)
698
+					buf = dbi.writeSetTo(buf, now)
685 699
 					n++
686 700
 					return true
687 701
 				},
@@ -908,8 +922,8 @@ func (db *DB) readLoad(rd io.Reader, modTime time.Time) (n int64, err error) {
908 922
 			db.deleteFromDatabase(&dbItem{key: parts[1]})
909 923
 		} else if (parts[0][0] == 'f' || parts[0][0] == 'F') &&
910 924
 			strings.ToLower(parts[0]) == "flushdb" {
911
-			db.keys = btree.New(lessCtx(nil))
912
-			db.exps = btree.New(lessCtx(&exctx{db}))
925
+			db.keys = btreeNew(lessCtx(nil))
926
+			db.exps = btreeNew(lessCtx(&exctx{db}))
913 927
 			db.idxs = make(map[string]*index)
914 928
 		} else {
915 929
 			return totalSize, ErrInvalid
@@ -941,11 +955,16 @@ func (db *DB) load() error {
941 955
 			return err
942 956
 		}
943 957
 	}
944
-	pos, err := db.file.Seek(n, 0)
945
-	if err != nil {
958
+	if _, err := db.file.Seek(n, 0); err != nil {
946 959
 		return err
947 960
 	}
948
-	db.lastaofsz = int(pos)
961
+	var estaofsz int
962
+	db.keys.Walk(func(items []interface{}) {
963
+		for _, v := range items {
964
+			estaofsz += v.(*dbItem).estAOFSetSize()
965
+		}
966
+	})
967
+	db.lastaofsz += estaofsz
949 968
 	return nil
950 969
 }
951 970
 
@@ -1054,8 +1073,8 @@ func (tx *Tx) DeleteAll() error {
1054 1073
 	}
1055 1074
 
1056 1075
 	// now reset the live database trees
1057
-	tx.db.keys = btree.New(lessCtx(nil))
1058
-	tx.db.exps = btree.New(lessCtx(&exctx{tx.db}))
1076
+	tx.db.keys = btreeNew(lessCtx(nil))
1077
+	tx.db.exps = btreeNew(lessCtx(&exctx{tx.db}))
1059 1078
 	tx.db.idxs = make(map[string]*index)
1060 1079
 
1061 1080
 	// finally re-create the indexes
@@ -1165,12 +1184,13 @@ func (tx *Tx) Commit() error {
1165 1184
 		if tx.wc.rbkeys != nil {
1166 1185
 			tx.db.buf = append(tx.db.buf, "*1\r\n$7\r\nflushdb\r\n"...)
1167 1186
 		}
1187
+		now := time.Now()
1168 1188
 		// Each committed record is written to disk
1169 1189
 		for key, item := range tx.wc.commitItems {
1170 1190
 			if item == nil {
1171 1191
 				tx.db.buf = (&dbItem{key: key}).writeDeleteTo(tx.db.buf)
1172 1192
 			} else {
1173
-				tx.db.buf = item.writeSetTo(tx.db.buf)
1193
+				tx.db.buf = item.writeSetTo(tx.db.buf, now)
1174 1194
 			}
1175 1195
 		}
1176 1196
 		// Flushing the buffer only once per transaction.
@@ -1243,16 +1263,53 @@ type dbItem struct {
1243 1263
 	keyless  bool        // keyless item for scanning
1244 1264
 }
1245 1265
 
1266
+func estIntSize(x int) int {
1267
+	if x == 0 {
1268
+		return 1
1269
+	}
1270
+	var n int
1271
+	for x > 0 {
1272
+		n++
1273
+		x /= 10
1274
+	}
1275
+	return n
1276
+}
1277
+
1278
+func estArraySize(count int) int {
1279
+	return 1 + estIntSize(count) + 2
1280
+}
1281
+
1282
+func estBulkStringSize(s string) int {
1283
+	return 1 + estIntSize(len(s)) + 2 + len(s) + 2
1284
+}
1285
+
1286
+func (dbi *dbItem) estAOFSetSize() (n int) {
1287
+	if dbi.opts != nil && dbi.opts.ex {
1288
+		n += estArraySize(5)
1289
+		n += estBulkStringSize("set")
1290
+		n += estBulkStringSize(dbi.key)
1291
+		n += estBulkStringSize(dbi.val)
1292
+		n += estBulkStringSize("ex")
1293
+		n += estBulkStringSize("99") // estimate two byte bulk string
1294
+	} else {
1295
+		n += estArraySize(3)
1296
+		n += estBulkStringSize("set")
1297
+		n += estBulkStringSize(dbi.key)
1298
+		n += estBulkStringSize(dbi.val)
1299
+	}
1300
+	return n
1301
+}
1302
+
1246 1303
 func appendArray(buf []byte, count int) []byte {
1247 1304
 	buf = append(buf, '*')
1248
-	buf = append(buf, strconv.FormatInt(int64(count), 10)...)
1305
+	buf = strconv.AppendInt(buf, int64(count), 10)
1249 1306
 	buf = append(buf, '\r', '\n')
1250 1307
 	return buf
1251 1308
 }
1252 1309
 
1253 1310
 func appendBulkString(buf []byte, s string) []byte {
1254 1311
 	buf = append(buf, '$')
1255
-	buf = append(buf, strconv.FormatInt(int64(len(s)), 10)...)
1312
+	buf = strconv.AppendInt(buf, int64(len(s)), 10)
1256 1313
 	buf = append(buf, '\r', '\n')
1257 1314
 	buf = append(buf, s...)
1258 1315
 	buf = append(buf, '\r', '\n')
@@ -1260,9 +1317,9 @@ func appendBulkString(buf []byte, s string) []byte {
1260 1317
 }
1261 1318
 
1262 1319
 // writeSetTo writes an item as a single SET record to the a bufio Writer.
1263
-func (dbi *dbItem) writeSetTo(buf []byte) []byte {
1320
+func (dbi *dbItem) writeSetTo(buf []byte, now time.Time) []byte {
1264 1321
 	if dbi.opts != nil && dbi.opts.ex {
1265
-		ex := time.Until(dbi.opts.exat) / time.Second
1322
+		ex := dbi.opts.exat.Sub(now) / time.Second
1266 1323
 		buf = appendArray(buf, 5)
1267 1324
 		buf = appendBulkString(buf, "set")
1268 1325
 		buf = appendBulkString(buf, dbi.key)
@@ -2300,3 +2357,8 @@ func btreeDescendLessOrEqual(tr *btree.BTree, pivot interface{},
2300 2357
 ) {
2301 2358
 	tr.Descend(pivot, iter)
2302 2359
 }
2360
+
2361
+func btreeNew(less func(a, b interface{}) bool) *btree.BTree {
2362
+	// Using NewNonConcurrent because we're managing our own locks.
2363
+	return btree.NewNonConcurrent(less)
2364
+}

+ 3
- 3
vendor/github.com/tidwall/buntdb/go.mod View File

@@ -3,9 +3,9 @@ module github.com/tidwall/buntdb
3 3
 go 1.16
4 4
 
5 5
 require (
6
-	github.com/tidwall/btree v0.4.2
7
-	github.com/tidwall/gjson v1.7.4
8
-	github.com/tidwall/grect v0.1.1
6
+	github.com/tidwall/btree v0.6.0
7
+	github.com/tidwall/gjson v1.8.0
8
+	github.com/tidwall/grect v0.1.2
9 9
 	github.com/tidwall/lotsa v1.0.2
10 10
 	github.com/tidwall/match v1.0.3
11 11
 	github.com/tidwall/rtred v0.1.2

+ 6
- 6
vendor/github.com/tidwall/buntdb/go.sum View File

@@ -1,9 +1,9 @@
1
-github.com/tidwall/btree v0.4.2 h1:aLwwJlG+InuFzdAPuBf9YCAR1LvSQ9zhC5aorFPlIPs=
2
-github.com/tidwall/btree v0.4.2/go.mod h1:huei1BkDWJ3/sLXmO+bsCNELL+Bp2Kks9OLyQFkzvA8=
3
-github.com/tidwall/gjson v1.7.4 h1:19cchw8FOxkG5mdLRkGf9jqIqEyqdZhPqW60XfyFxk8=
4
-github.com/tidwall/gjson v1.7.4/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
5
-github.com/tidwall/grect v0.1.1 h1:+kMEkxhoqB7rniVXzMEIA66XwU07STgINqxh+qVIndY=
6
-github.com/tidwall/grect v0.1.1/go.mod h1:CzvbGiFbWUwiJ1JohXLb28McpyBsI00TK9Y6pDWLGRQ=
1
+github.com/tidwall/btree v0.6.0 h1:JLYAFGV+1gjyFi3iQbO/fupBin+Ooh7dxqVV0twJ1Bo=
2
+github.com/tidwall/btree v0.6.0/go.mod h1:TzIRzen6yHbibdSfK6t8QimqbUnoxUSrZfeW7Uob0q4=
3
+github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ=
4
+github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
5
+github.com/tidwall/grect v0.1.2 h1:wKVeQVZhjaFCKTTlpkDe3Ex4ko3cMGW3MRKawRe8uQ4=
6
+github.com/tidwall/grect v0.1.2/go.mod h1:v+n4ewstPGduVJebcp5Eh2WXBJBumNzyhK8GZt4gHNw=
7 7
 github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8=
8 8
 github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8=
9 9
 github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=

+ 4
- 6
vendor/github.com/tidwall/gjson/README.md View File

@@ -14,8 +14,6 @@ It has features such as [one line retrieval](#get-a-value), [dot notation paths]
14 14
 
15 15
 Also check out [SJSON](https://github.com/tidwall/sjson) for modifying json, and the [JJ](https://github.com/tidwall/jj) command line tool.
16 16
 
17
-For the Rust version go to [gjson.rs](https://github.com/tidwall/gjson.rs).
18
-
19 17
 Getting Started
20 18
 ===============
21 19
 
@@ -152,10 +150,6 @@ result.Less(token Result, caseSensitive bool) bool
152 150
 
153 151
 The `result.Value()` function returns an `interface{}` which requires type assertion and is one of the following Go types:
154 152
 
155
-The `result.Array()` function returns back an array of values.
156
-If the result represents a non-existent value, then an empty array will be returned.
157
-If the result is not a JSON array, the return value will be an array containing one result.
158
-
159 153
 ```go
160 154
 boolean >> bool
161 155
 number  >> float64
@@ -165,6 +159,10 @@ array   >> []interface{}
165 159
 object  >> map[string]interface{}
166 160
 ```
167 161
 
162
+The `result.Array()` function returns back an array of values.
163
+If the result represents a non-existent value, then an empty array will be returned.
164
+If the result is not a JSON array, the return value will be an array containing one result.
165
+
168 166
 ### 64-bit integers
169 167
 
170 168
 The `result.Int()` and `result.Uint()` calls are capable of reading all 64 bits, allowing for large JSON integers.

+ 33
- 119
vendor/github.com/tidwall/gjson/gjson.go View File

@@ -714,10 +714,10 @@ type arrayPathResult struct {
714 714
 	alogkey string
715 715
 	query   struct {
716 716
 		on    bool
717
+		all   bool
717 718
 		path  string
718 719
 		op    string
719 720
 		value string
720
-		all   bool
721 721
 	}
722 722
 }
723 723
 
@@ -750,120 +750,27 @@ func parseArrayPath(path string) (r arrayPathResult) {
750 750
 				} else if path[1] == '[' || path[1] == '(' {
751 751
 					// query
752 752
 					r.query.on = true
753
-					if true {
754
-						qpath, op, value, _, fi, ok := parseQuery(path[i:])
755
-						if !ok {
756
-							// bad query, end now
757
-							break
758
-						}
759
-						r.query.path = qpath
760
-						r.query.op = op
761
-						r.query.value = value
762
-						i = fi - 1
763
-						if i+1 < len(path) && path[i+1] == '#' {
764
-							r.query.all = true
765
-						}
766
-					} else {
767
-						var end byte
768
-						if path[1] == '[' {
769
-							end = ']'
770
-						} else {
771
-							end = ')'
772
-						}
773
-						i += 2
774
-						// whitespace
775
-						for ; i < len(path); i++ {
776
-							if path[i] > ' ' {
777
-								break
778
-							}
779
-						}
780
-						s := i
781
-						for ; i < len(path); i++ {
782
-							if path[i] <= ' ' ||
783
-								path[i] == '!' ||
784
-								path[i] == '=' ||
785
-								path[i] == '<' ||
786
-								path[i] == '>' ||
787
-								path[i] == '%' ||
788
-								path[i] == end {
789
-								break
790
-							}
791
-						}
792
-						r.query.path = path[s:i]
793
-						// whitespace
794
-						for ; i < len(path); i++ {
795
-							if path[i] > ' ' {
796
-								break
797
-							}
798
-						}
799
-						if i < len(path) {
800
-							s = i
801
-							if path[i] == '!' {
802
-								if i < len(path)-1 && (path[i+1] == '=' ||
803
-									path[i+1] == '%') {
804
-									i++
805
-								}
806
-							} else if path[i] == '<' || path[i] == '>' {
807
-								if i < len(path)-1 && path[i+1] == '=' {
808
-									i++
809
-								}
810
-							} else if path[i] == '=' {
811
-								if i < len(path)-1 && path[i+1] == '=' {
812
-									s++
813
-									i++
814
-								}
815
-							}
816
-							i++
817
-							r.query.op = path[s:i]
818
-							// whitespace
819
-							for ; i < len(path); i++ {
820
-								if path[i] > ' ' {
821
-									break
822
-								}
823
-							}
824
-							s = i
825
-							for ; i < len(path); i++ {
826
-								if path[i] == '"' {
827
-									i++
828
-									s2 := i
829
-									for ; i < len(path); i++ {
830
-										if path[i] > '\\' {
831
-											continue
832
-										}
833
-										if path[i] == '"' {
834
-											// look for an escaped slash
835
-											if path[i-1] == '\\' {
836
-												n := 0
837
-												for j := i - 2; j > s2-1; j-- {
838
-													if path[j] != '\\' {
839
-														break
840
-													}
841
-													n++
842
-												}
843
-												if n%2 == 0 {
844
-													continue
845
-												}
846
-											}
847
-											break
848
-										}
849
-									}
850
-								} else if path[i] == end {
851
-									if i+1 < len(path) && path[i+1] == '#' {
852
-										r.query.all = true
853
-									}
854
-									break
855
-								}
856
-							}
857
-							if i > len(path) {
858
-								i = len(path)
859
-							}
860
-							v := path[s:i]
861
-							for len(v) > 0 && v[len(v)-1] <= ' ' {
862
-								v = v[:len(v)-1]
863
-							}
864
-							r.query.value = v
753
+					qpath, op, value, _, fi, vesc, ok :=
754
+						parseQuery(path[i:])
755
+					if !ok {
756
+						// bad query, end now
757
+						break
758
+					}
759
+					if len(value) > 2 && value[0] == '"' &&
760
+						value[len(value)-1] == '"' {
761
+						value = value[1 : len(value)-1]
762
+						if vesc {
763
+							value = unescape(value)
865 764
 						}
866 765
 					}
766
+					r.query.path = qpath
767
+					r.query.op = op
768
+					r.query.value = value
769
+
770
+					i = fi - 1
771
+					if i+1 < len(path) && path[i+1] == '#' {
772
+						r.query.all = true
773
+					}
867 774
 				}
868 775
 			}
869 776
 			continue
@@ -889,11 +796,11 @@ func parseArrayPath(path string) (r arrayPathResult) {
889 796
 //                              # middle
890 797
 //   .cap                       # right
891 798
 func parseQuery(query string) (
892
-	path, op, value, remain string, i int, ok bool,
799
+	path, op, value, remain string, i int, vesc, ok bool,
893 800
 ) {
894 801
 	if len(query) < 2 || query[0] != '#' ||
895 802
 		(query[1] != '(' && query[1] != '[') {
896
-		return "", "", "", "", i, false
803
+		return "", "", "", "", i, false, false
897 804
 	}
898 805
 	i = 2
899 806
 	j := 0 // start of value part
@@ -921,6 +828,7 @@ func parseQuery(query string) (
921 828
 			i++
922 829
 			for ; i < len(query); i++ {
923 830
 				if query[i] == '\\' {
831
+					vesc = true
924 832
 					i++
925 833
 				} else if query[i] == '"' {
926 834
 					break
@@ -929,7 +837,7 @@ func parseQuery(query string) (
929 837
 		}
930 838
 	}
931 839
 	if depth > 0 {
932
-		return "", "", "", "", i, false
840
+		return "", "", "", "", i, false, false
933 841
 	}
934 842
 	if j > 0 {
935 843
 		path = trim(query[2:j])
@@ -966,7 +874,7 @@ func parseQuery(query string) (
966 874
 		path = trim(query[2:i])
967 875
 		remain = query[i+1:]
968 876
 	}
969
-	return path, op, value, remain, i + 1, true
877
+	return path, op, value, remain, i + 1, vesc, true
970 878
 }
971 879
 
972 880
 func trim(s string) string {
@@ -1266,8 +1174,14 @@ func parseObject(c *parseContext, i int, path string) (int, bool) {
1266 1174
 }
1267 1175
 func queryMatches(rp *arrayPathResult, value Result) bool {
1268 1176
 	rpv := rp.query.value
1269
-	if len(rpv) > 2 && rpv[0] == '"' && rpv[len(rpv)-1] == '"' {
1270
-		rpv = rpv[1 : len(rpv)-1]
1177
+	if len(rpv) > 0 && rpv[0] == '~' {
1178
+		// convert to bool
1179
+		rpv = rpv[1:]
1180
+		if value.Bool() {
1181
+			value = Result{Type: True}
1182
+		} else {
1183
+			value = Result{Type: False}
1184
+		}
1271 1185
 	}
1272 1186
 	if !value.Exists() {
1273 1187
 		return false

+ 1
- 1
vendor/github.com/tidwall/grect/go.mod View File

@@ -2,4 +2,4 @@ module github.com/tidwall/grect
2 2
 
3 3
 go 1.15
4 4
 
5
-require github.com/tidwall/gjson v1.7.4
5
+require github.com/tidwall/gjson v1.8.0

+ 2
- 2
vendor/github.com/tidwall/grect/go.sum View File

@@ -1,5 +1,5 @@
1
-github.com/tidwall/gjson v1.7.4 h1:19cchw8FOxkG5mdLRkGf9jqIqEyqdZhPqW60XfyFxk8=
2
-github.com/tidwall/gjson v1.7.4/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
1
+github.com/tidwall/gjson v1.8.0 h1:Qt+orfosKn0rbNTZqHYDqBrmm3UDA4KRkv70fDzG+PQ=
2
+github.com/tidwall/gjson v1.8.0/go.mod h1:5/xDoumyyDNerp2U36lyolv46b3uF/9Bu6OfyQ9GImk=
3 3
 github.com/tidwall/match v1.0.3 h1:FQUVvBImDutD8wJLN6c5eMzWtjgONK9MwIBCOrUJKeE=
4 4
 github.com/tidwall/match v1.0.3/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
5 5
 github.com/tidwall/pretty v1.1.0 h1:K3hMW5epkdAVwibsQEfR/7Zj0Qgt4DxtNumTq/VloO8=

+ 4
- 14
vendor/modules.txt View File

@@ -7,8 +7,6 @@ github.com/GehirnInc/crypt
7 7
 github.com/GehirnInc/crypt/common
8 8
 github.com/GehirnInc/crypt/internal
9 9
 github.com/GehirnInc/crypt/md5_crypt
10
-# github.com/dgrijalva/jwt-go v3.2.0+incompatible
11
-## explicit
12 10
 # github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815
13 11
 ## explicit
14 12
 github.com/docopt/docopt-go
@@ -35,8 +33,6 @@ github.com/golang-jwt/jwt
35 33
 # github.com/gorilla/websocket v1.4.2 => github.com/ergochat/websocket v1.4.2-oragono1
36 34
 ## explicit
37 35
 github.com/gorilla/websocket
38
-# github.com/goshuirc/irc-go v0.0.0-20210318074529-bdc2c2cd2fef
39
-## explicit
40 36
 # github.com/okzk/sdnotify v0.0.0-20180710141335-d9becc38acbd
41 37
 ## explicit
42 38
 github.com/okzk/sdnotify
@@ -44,20 +40,16 @@ github.com/okzk/sdnotify
44 40
 ## explicit
45 41
 # github.com/onsi/gomega v1.9.0
46 42
 ## explicit
47
-# github.com/oragono/confusables v0.0.0-20201108231250-4ab98ab61fb1
48
-## explicit
49
-# github.com/oragono/go-ident v0.0.0-20200511222032-830550b1d775
50
-## explicit
51 43
 # github.com/stretchr/testify v1.4.0
52 44
 ## explicit
53
-# github.com/tidwall/btree v0.4.2
45
+# github.com/tidwall/btree v0.6.0
54 46
 github.com/tidwall/btree
55
-# github.com/tidwall/buntdb v1.2.3
47
+# github.com/tidwall/buntdb v1.2.6
56 48
 ## explicit
57 49
 github.com/tidwall/buntdb
58
-# github.com/tidwall/gjson v1.7.4
50
+# github.com/tidwall/gjson v1.8.0
59 51
 github.com/tidwall/gjson
60
-# github.com/tidwall/grect v0.1.1
52
+# github.com/tidwall/grect v0.1.2
61 53
 github.com/tidwall/grect
62 54
 # github.com/tidwall/match v1.0.3
63 55
 github.com/tidwall/match
@@ -66,8 +58,6 @@ github.com/tidwall/pretty
66 58
 # github.com/tidwall/rtred v0.1.2
67 59
 github.com/tidwall/rtred
68 60
 github.com/tidwall/rtred/base
69
-# github.com/tidwall/rtree v0.0.0-20201027154624-32188eeb08a8
70
-## explicit
71 61
 # github.com/tidwall/tinyqueue v0.1.1
72 62
 github.com/tidwall/tinyqueue
73 63
 # github.com/toorop/go-dkim v0.0.0-20201103131630-e1cd1a0a5208

Loading…
Cancel
Save