├── .editorconfig ├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── data ├── copy.go ├── item.go ├── iter.go ├── list.go ├── list_test.go ├── node.go ├── tree.go └── tree_test.go ├── db.go ├── err.go ├── go.mod ├── go.sum ├── it.go ├── kv.go ├── main.go ├── main_test.go ├── op.go ├── tx.go └── util.go /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig coding styles definitions. For more information about the 2 | # properties used in this file, please see the EditorConfig documentation: 3 | # http://editorconfig.org/ 4 | 5 | root = true 6 | 7 | [*] 8 | charset = utf-8 9 | end_of_line = LF 10 | indent_size = 4 11 | indent_style = tab 12 | insert_final_newline = true 13 | trim_trailing_whitespace = true 14 | 15 | [*.{yml,json}] 16 | indent_size = 2 17 | indent_style = space 18 | 19 | [*.{md,diff}] 20 | trim_trailing_whitespace = false 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # ----------------------------------- 2 | # OS X 3 | # ----------------------------------- 4 | 5 | # Directory files 6 | .DS_Store 7 | .AppleDouble 8 | .LSOverride 9 | 10 | # Thumbnail files 11 | ._* 12 | 13 | # Files that might appear on external disk 14 | .Spotlight-V100 15 | .Trashes 16 | 17 | # Directories potentially created on remote AFP share 18 | .AppleDB 19 | .AppleDesktop 20 | Network Trash Folder 21 | Temporary Items 22 | .apdisk 23 | 24 | # ----------------------------------- 25 | # Files 26 | # ----------------------------------- 27 | 28 | *.test 29 | *.cover 30 | 31 | # ----------------------------------- 32 | # Folders 33 | # ----------------------------------- 34 | 35 | app/ 36 | dev/ 37 | vendor/ 38 | node_modules/ 39 | bower_components/ 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright © 2021 SurrealDB Ltd. 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright © 2021 SurrealDB Ltd 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | GO ?= CGO_ENABLED=0 go 16 | 17 | .PHONY: default 18 | default: 19 | @echo "Choose a Makefile target:" 20 | @$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) : 2>/dev/null | awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print " - " $$1}}' | sort 21 | 22 | .PHONY: clean 23 | clean: 24 | $(GO) clean -i 25 | 26 | .PHONY: tests 27 | tests: 28 | $(GO) test ./... 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RixxDB 2 | 3 | RixxDB is a versioned, embedded, strongly-consistent, key-value database. 4 | 5 | [![](https://img.shields.io/badge/status-alpha-ff00bb.svg?style=flat-square)](https://github.com/surrealdb/rixxdb) [![](https://img.shields.io/badge/godoc-reference-blue.svg?style=flat-square)](https://godoc.org/github.com/surrealdb/rixxdb) [![](https://goreportcard.com/badge/github.com/surrealdb/rixxdb?style=flat-square)](https://goreportcard.com/report/github.com/surrealdb/rixxdb) [![](https://img.shields.io/badge/license-Apache_License_2.0-00bfff.svg?style=flat-square)](https://github.com/surrealdb/rixxdb) 6 | 7 | #### Features 8 | 9 | - In-memory database 10 | - Built-in encryption 11 | - Built-in compression 12 | - Built-in item versioning 13 | - Multi-version concurrency control 14 | - Rich transaction support with rollbacks 15 | - Multiple concurrent readers without locking 16 | - Atomicity, Consistency and Isolation from ACID 17 | - Durable, configurable append-only file format for data persistence 18 | - Flexible iteration of data; ascending, descending, ranges, and hierarchical ranges 19 | 20 | #### Installation 21 | 22 | ```bash 23 | go get github.com/surrealdb/rixxdb 24 | ``` 25 | -------------------------------------------------------------------------------- /data/copy.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | // Copy is a copy of a tree which can be used to apply changes to 8 | // the radix tree. All changes are applied atomically and a new tree 9 | // is returned when committed. A Copy is not thread safe. 10 | type Copy struct { 11 | size int 12 | root *Node 13 | } 14 | 15 | // Size is used to return the total number of elements in the tree. 16 | func (c *Copy) Size() int { 17 | return c.size 18 | } 19 | 20 | // Root returns the root of the radix tree within this tree copy. 21 | func (c *Copy) Root() *Node { 22 | return c.root 23 | } 24 | 25 | // Tree returns a new tree with the changes committed in memory. 26 | func (c *Copy) Tree() *Tree { 27 | return &Tree{c.size, c.root} 28 | } 29 | 30 | // Cursor returns a new cursor for iterating through the radix tree. 31 | func (c *Copy) Cursor() *Cursor { 32 | return &Cursor{tree: c} 33 | } 34 | 35 | // All is used to retrieve a specific key, returning all list values. 36 | func (c *Copy) All(key []byte) *List { 37 | return c.root.get(key) 38 | } 39 | 40 | // Cut is used to delete a given key, returning the previous value. 41 | func (c *Copy) Cut(key []byte) *Item { 42 | root, leaf, old := c.del(nil, c.root, key) 43 | if root != nil { 44 | c.root = root 45 | } 46 | if leaf != nil { 47 | c.size-- 48 | } 49 | return old 50 | } 51 | 52 | // Get is used to retrieve a specific key, returning the current value. 53 | func (c *Copy) Get(ver uint64, key []byte) *Item { 54 | if val := c.root.get(key); val != nil { 55 | return val.Get(ver, Upto) 56 | } 57 | return nil 58 | } 59 | 60 | // Del is used to delete a given key, returning the previous value. 61 | func (c *Copy) Del(ver uint64, key []byte) *Item { 62 | if val := c.root.get(key); val != nil { 63 | return val.Put(ver, nil) 64 | } 65 | return nil 66 | } 67 | 68 | // Put is used to insert a specific key, returning the previous value. 69 | func (c *Copy) Put(ver uint64, key, val []byte) *Item { 70 | root, leaf, old := c.put(nil, c.root, ver, key, key, val) 71 | if root != nil { 72 | c.root = root 73 | } 74 | if leaf == nil { 75 | c.size++ 76 | } 77 | return old 78 | } 79 | 80 | // --------------------------------------------------------------------------- 81 | 82 | func prefix(a, b []byte) (i int) { 83 | for i = 0; i < len(a) && i < len(b); i++ { 84 | if a[i] != b[i] { 85 | break 86 | } 87 | } 88 | return 89 | } 90 | 91 | func concat(a, b []byte) (c []byte) { 92 | c = make([]byte, len(a)+len(b)) 93 | copy(c, a) 94 | copy(c[len(a):], b) 95 | return 96 | } 97 | 98 | func (c *Copy) del(p, n *Node, s []byte) (*Node, *leaf, *Item) { 99 | 100 | if len(s) == 0 { 101 | 102 | if !n.isLeaf() { 103 | return nil, nil, nil 104 | } 105 | 106 | d := n.dup() 107 | 108 | // Remove the leaf node 109 | d.leaf = nil 110 | 111 | // Check if the node should be merged 112 | if n != c.root && len(d.edges) == 1 { 113 | d.mergeChild() 114 | } 115 | 116 | // Return the found node and leaf node 117 | return d, n.leaf, n.leaf.val.Max() 118 | 119 | } 120 | 121 | // Look for an edge 122 | l := s[0] 123 | i, e := n.getSub(l) 124 | if e == nil || !bytes.HasPrefix(s, e.prefix) { 125 | return nil, nil, nil 126 | } 127 | 128 | // Consume the search prefix 129 | s = s[len(e.prefix):] 130 | 131 | node, leaf, old := c.del(n, e, s) 132 | if node == nil { 133 | return nil, nil, nil 134 | } 135 | 136 | // Copy this node 137 | d := n.dup() 138 | 139 | // Delete the edge if the node has no edges 140 | if node.leaf == nil && len(node.edges) == 0 { 141 | d.delSub(l) 142 | if n != c.root && len(d.edges) == 1 && !d.isLeaf() { 143 | d.mergeChild() 144 | } 145 | } else { 146 | d.edges[i] = node 147 | } 148 | 149 | return d, leaf, old 150 | 151 | } 152 | 153 | func (c *Copy) put(p, n *Node, t uint64, s, k, v []byte) (*Node, *leaf, *Item) { 154 | 155 | if len(s) == 0 { 156 | 157 | d := n.dup() 158 | 159 | // Create the leaf if necessary 160 | if !n.isLeaf() { 161 | d.leaf = &leaf{key: k, val: new(List)} 162 | } 163 | 164 | // Return the new node and leaf node 165 | return d, n.leaf, d.leaf.val.Put(t, v) 166 | 167 | } 168 | 169 | // Look for the edge 170 | i, e := n.getSub(s[0]) 171 | 172 | // No edge, create one 173 | if e == nil { 174 | e := &Node{ 175 | leaf: &leaf{ 176 | key: k, 177 | val: new(List), 178 | }, 179 | prefix: s, 180 | } 181 | e.leaf.val.Put(t, v) 182 | d := n.dup() 183 | d.addSub(e) 184 | return d, nil, nil 185 | } 186 | 187 | // Determine longest prefix of the search key on match 188 | cl := prefix(s, e.prefix) 189 | 190 | if cl == len(e.prefix) { 191 | s = s[cl:] 192 | node, leaf, old := c.put(n, e, t, s, k, v) 193 | if node != nil { 194 | nc := n.dup() 195 | nc.edges[i] = node 196 | return nc, leaf, old 197 | } 198 | return nil, leaf, old 199 | } 200 | 201 | // Split the node 202 | nc := n.dup() 203 | splitNode := &Node{ 204 | prefix: s[:cl], 205 | } 206 | nc.repSub(splitNode) 207 | 208 | // Restore the existing child node 209 | modChild := e.dup() 210 | splitNode.addSub(modChild) 211 | modChild.prefix = modChild.prefix[cl:] 212 | 213 | // Create a new leaf node 214 | leaf := &leaf{ 215 | key: k, 216 | val: new(List), 217 | } 218 | leaf.val.Put(t, v) 219 | 220 | // If the new key is a subset, add to to this node 221 | s = s[cl:] 222 | if len(s) == 0 { 223 | splitNode.leaf = leaf 224 | return nc, nil, nil 225 | } 226 | 227 | // Create a new edge for the node 228 | splitNode.addSub(&Node{ 229 | leaf: leaf, 230 | prefix: s, 231 | }) 232 | 233 | return nc, nil, nil 234 | 235 | } 236 | -------------------------------------------------------------------------------- /data/item.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | // Item represents an item in a time-series list. 4 | type Item struct { 5 | ver uint64 6 | val []byte 7 | list *List 8 | prev *Item 9 | next *Item 10 | } 11 | 12 | // Ver returns the version of this item in the containing list. 13 | func (i *Item) Ver() uint64 { 14 | return i.ver 15 | } 16 | 17 | // Val returns the value of this item in the containing list. 18 | func (i *Item) Val() []byte { 19 | return i.val 20 | } 21 | 22 | // Set updates the value of this item in the containing list. 23 | func (i *Item) Set(val []byte) *Item { 24 | i.val = val 25 | return i 26 | } 27 | 28 | // Del deletes the item from any containing list and returns it. 29 | func (i *Item) Del() *Item { 30 | 31 | if i.list != nil { 32 | 33 | i.list.lock.Lock() 34 | defer i.list.lock.Unlock() 35 | 36 | if i.prev != nil && i.next != nil { 37 | i.prev.next = i.next 38 | i.next.prev = i.prev 39 | i.prev = nil 40 | i.next = nil 41 | } else if i.prev != nil { 42 | i.list.max = i.prev 43 | i.prev.next = nil 44 | i.prev = nil 45 | } else if i.next != nil { 46 | i.list.min = i.next 47 | i.next.prev = nil 48 | i.next = nil 49 | } else { 50 | i.list.min = nil 51 | i.list.max = nil 52 | } 53 | 54 | i.list.size-- 55 | 56 | i.list = nil 57 | 58 | } 59 | 60 | return i 61 | 62 | } 63 | 64 | // Prev returns the previous item to this item in the list. 65 | func (i *Item) Prev() *Item { 66 | 67 | if i.prev != nil { 68 | 69 | i.list.lock.RLock() 70 | defer i.list.lock.RUnlock() 71 | 72 | return i.prev 73 | 74 | } 75 | 76 | return nil 77 | 78 | } 79 | 80 | // Next returns the next item to this item in the list. 81 | func (i *Item) Next() *Item { 82 | 83 | if i.next != nil { 84 | 85 | i.list.lock.RLock() 86 | defer i.list.lock.RUnlock() 87 | 88 | return i.next 89 | 90 | } 91 | 92 | return nil 93 | 94 | } 95 | -------------------------------------------------------------------------------- /data/iter.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | // Cursor represents an iterator that can traverse over all key-value 8 | // pairs in a tree in sorted order. Cursors can be obtained from a 9 | // transaction and are valid as long as the transaction is open. 10 | // Changing data while traversing with a cursor may cause it to be 11 | // invalidated and return unexpected keys and/or values. You must 12 | // reposition your cursor after mutating data. 13 | type Cursor struct { 14 | tree *Copy 15 | seek []byte 16 | nums []int 17 | path []*Node 18 | } 19 | 20 | // Here moves the cursor to the first item in the tree and returns 21 | // its key and value. If the tree is empty then a nil key and value 22 | // are returned. 23 | func (c *Cursor) Here() *List { 24 | 25 | _, val := c.Seek(c.seek) 26 | 27 | return val 28 | 29 | } 30 | 31 | // Del removes the current item under the cursor from the tree. If 32 | // the cursor has not yet been positioned using First, Last, or Seek, 33 | // then no item is deleted and a nil key and value are returned. 34 | func (c *Cursor) Del() ([]byte, *List) { 35 | 36 | _, val := c.Seek(c.seek) 37 | 38 | c.tree.Del(0, c.seek) 39 | 40 | return c.seek, val 41 | 42 | } 43 | 44 | // First moves the cursor to the first item in the tree and returns 45 | // its key and value. If the tree is empty then a nil key and value 46 | // are returned. 47 | func (c *Cursor) First() ([]byte, *List) { 48 | 49 | c.nums = c.nums[:0] 50 | c.path = c.path[:0] 51 | 52 | return c.first(c.tree.root) 53 | 54 | } 55 | 56 | // Last moves the cursor to the last item in the tree and returns its 57 | // key and value. If the tree is empty then a nil key and value are 58 | // returned. 59 | func (c *Cursor) Last() ([]byte, *List) { 60 | 61 | c.nums = c.nums[:0] 62 | c.path = c.path[:0] 63 | 64 | return c.last(c.tree.root) 65 | 66 | } 67 | 68 | // Prev moves the cursor to the previous item in the tree and returns 69 | // its key and value. If the tree is empty then a nil key and value are 70 | // returned, and if the cursor is at the start of the tree then a nil key 71 | // and value are returned. If the cursor has not yet been positioned 72 | // using First, Last, or Seek, then a nil key and value are returned. 73 | func (c *Cursor) Prev() ([]byte, *List) { 74 | 75 | OUTER: 76 | for { 77 | 78 | if len(c.path) == 0 { 79 | break 80 | } 81 | 82 | // ------------------------------ 83 | // Decrease counter 84 | // ------------------------------ 85 | 86 | for { 87 | 88 | x := len(c.nums) - 1 89 | 90 | if c.nums[x] == 0 { 91 | 92 | c.nums = c.nums[:x] 93 | c.path = c.path[:x] 94 | 95 | if len(c.nums) == 0 { 96 | break OUTER 97 | } 98 | 99 | n := c.node() 100 | 101 | if n.isLeaf() { 102 | c.seek = n.leaf.key 103 | return n.leaf.key, n.leaf.val 104 | } 105 | 106 | continue 107 | 108 | } 109 | 110 | break 111 | 112 | } 113 | 114 | // ------------------------------ 115 | // Decrease edges 116 | // ------------------------------ 117 | 118 | for { 119 | 120 | x := len(c.nums) - 1 121 | 122 | if c.nums[x]-1 >= 0 { 123 | 124 | c.nums[x]-- 125 | 126 | n := c.node() 127 | 128 | for { 129 | 130 | if num := len(n.edges); num > 0 { 131 | c.nums = append(c.nums, num-1) 132 | c.path = append(c.path, n) 133 | n = n.edges[num-1] 134 | continue 135 | } 136 | 137 | if n.isLeaf() { 138 | c.seek = n.leaf.key 139 | return n.leaf.key, n.leaf.val 140 | } 141 | 142 | continue OUTER 143 | 144 | } 145 | 146 | } 147 | 148 | } 149 | 150 | } 151 | 152 | return nil, nil 153 | 154 | } 155 | 156 | // Next moves the cursor to the next item in the tree and returns its 157 | // key and value. If the tree is empty then a nil key and value are 158 | // returned, and if the cursor is at the end of the tree then a nil key 159 | // and value are returned. If the cursor has not yet been positioned 160 | // using First, Last, or Seek, then a nil key and value are returned. 161 | func (c *Cursor) Next() ([]byte, *List) { 162 | 163 | OUTER: 164 | for { 165 | 166 | if len(c.nums) == 0 { 167 | break 168 | } 169 | 170 | n := c.node() 171 | 172 | // ------------------------------ 173 | // Increase edges 174 | // ------------------------------ 175 | 176 | for { 177 | 178 | if len(n.edges) > 0 { 179 | 180 | c.nums = append(c.nums, 0) 181 | c.path = append(c.path, n) 182 | n = n.edges[0] 183 | 184 | if n.isLeaf() { 185 | c.seek = n.leaf.key 186 | return n.leaf.key, n.leaf.val 187 | } 188 | 189 | continue 190 | 191 | } 192 | 193 | break 194 | 195 | } 196 | 197 | // ------------------------------ 198 | // Increase counter 199 | // ------------------------------ 200 | 201 | for { 202 | 203 | if len(c.nums) == 0 { 204 | break OUTER 205 | } 206 | 207 | x := len(c.nums) - 1 208 | 209 | if c.nums[x]+1 < len(c.path[x].edges) { 210 | 211 | c.nums[x]++ 212 | 213 | n = c.node() 214 | 215 | if n.isLeaf() { 216 | c.seek = n.leaf.key 217 | return n.leaf.key, n.leaf.val 218 | } 219 | 220 | continue OUTER 221 | 222 | } else { 223 | 224 | c.nums = c.nums[:x] 225 | c.path = c.path[:x] 226 | 227 | continue 228 | 229 | } 230 | 231 | } 232 | 233 | } 234 | 235 | return nil, nil 236 | 237 | } 238 | 239 | // Seek moves the cursor to a given key in the tree and returns it. 240 | // If the specified key does not exist then the next key in the tree 241 | // is used. If no keys follow, then a nil key and value are returned. 242 | func (c *Cursor) Seek(key []byte) ([]byte, *List) { 243 | 244 | s := key 245 | 246 | n := c.tree.root 247 | 248 | c.nums = c.nums[:0] 249 | c.path = c.path[:0] 250 | 251 | var x int 252 | 253 | // OUTER: 254 | for { 255 | 256 | // Check for key exhaution 257 | if len(s) == 0 { 258 | return c.first(n) 259 | } 260 | 261 | t := n 262 | 263 | // Look for an edge 264 | if x, n = n.getSub(s[0]); n == nil { 265 | 266 | if len(t.edges) == 0 { 267 | return c.Next() 268 | } else if s[0] < t.edges[0].prefix[0] { 269 | if len(c.path) == 0 { 270 | return c.first(c.tree.root) 271 | } 272 | return c.first(c.path[len(c.path)-1]) 273 | } else if s[0] > t.edges[len(t.edges)-1].prefix[0] { 274 | if len(c.path) == 0 { 275 | break 276 | } 277 | return c.last(c.path[len(c.path)-1]) 278 | } 279 | 280 | break 281 | 282 | } 283 | 284 | // Consume the search prefix 285 | if bytes.Compare(s, n.prefix) == 0 { 286 | c.nums = append(c.nums, x) 287 | c.path = append(c.path, t) 288 | s = s[:0] 289 | continue 290 | } else if bytes.HasPrefix(s, n.prefix) { 291 | c.nums = append(c.nums, x) 292 | c.path = append(c.path, t) 293 | s = s[len(n.prefix):] 294 | continue 295 | } else if bytes.HasPrefix(n.prefix, s) { 296 | c.nums = append(c.nums, x) 297 | c.path = append(c.path, t) 298 | s = s[:0] 299 | continue 300 | } else if bytes.Compare(s, n.prefix) < 0 { 301 | c.nums = append(c.nums, x) 302 | c.path = append(c.path, t) 303 | s = s[:0] 304 | continue 305 | } else if bytes.Compare(s, n.prefix) > 0 { 306 | c.nums = append(c.nums, x) 307 | c.path = append(c.path, t) 308 | c.last(n) 309 | return c.Next() 310 | } 311 | 312 | break 313 | 314 | } 315 | 316 | c.nums = c.nums[:0] 317 | c.path = c.path[:0] 318 | 319 | return nil, nil 320 | 321 | } 322 | 323 | // ------ 324 | 325 | func (c *Cursor) node() *Node { 326 | 327 | var x int 328 | 329 | x = len(c.nums) - 1 330 | 331 | if len(c.path[x].edges) <= c.nums[x] { 332 | c.Seek(c.seek) 333 | x = len(c.nums) - 1 334 | } 335 | 336 | return c.path[x].edges[c.nums[x]] 337 | 338 | } 339 | 340 | func (c *Cursor) first(n *Node) ([]byte, *List) { 341 | 342 | for { 343 | 344 | if n.isLeaf() { 345 | c.seek = n.leaf.key 346 | return n.leaf.key, n.leaf.val 347 | } 348 | 349 | if len(n.edges) > 0 { 350 | c.nums = append(c.nums, 0) 351 | c.path = append(c.path, n) 352 | n = n.edges[0] 353 | } else { 354 | break 355 | } 356 | 357 | } 358 | 359 | return nil, nil 360 | 361 | } 362 | 363 | func (c *Cursor) last(n *Node) ([]byte, *List) { 364 | 365 | for { 366 | 367 | if num := len(n.edges); num > 0 { 368 | c.nums = append(c.nums, num-1) 369 | c.path = append(c.path, n) 370 | n = n.edges[num-1] 371 | continue 372 | } 373 | 374 | if n.isLeaf() { 375 | c.seek = n.leaf.key 376 | return n.leaf.key, n.leaf.val 377 | } 378 | 379 | break 380 | 381 | } 382 | 383 | return nil, nil 384 | 385 | } 386 | -------------------------------------------------------------------------------- /data/list.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import "sync" 4 | 5 | // List represents a doubly-linked time-series list. 6 | type List struct { 7 | size int 8 | min *Item 9 | max *Item 10 | lock sync.RWMutex 11 | } 12 | 13 | // Find determines which method is used to seek items in the list. 14 | type Find int8 15 | 16 | const ( 17 | // Exact returns an item at a specific version from the list. If the exact 18 | // item does not exist in the list, then a nil value is returned. 19 | Exact Find = iota 20 | // Prev returns the nearest item in the list, where the version number is 21 | // less than the given version. In a time-series list, this can be used 22 | // to get the version that was valid before a specified time. 23 | Prev 24 | // Next returns the nearest item in the list, where the version number is 25 | // greater than the given version. In a time-series list, this can be used 26 | // to get the version that was changed after a specified time. 27 | Next 28 | // Upto returns the nearest item in the list, where the version number is 29 | // less than or equal to the given version. In a time-series list, this can 30 | // be used to get the version that was current at the specified time. 31 | Upto 32 | // Nearest returns an item nearest a specific version in the list. If there 33 | // is a previous version to the given version, then it will be returned, 34 | // otherwise it will return the next available version. 35 | Nearest 36 | ) 37 | 38 | // Clr clears all of the items from the list. 39 | func (l *List) Clr() { 40 | 41 | l.lock.Lock() 42 | defer l.lock.Unlock() 43 | 44 | l.size = 0 45 | l.min = nil 46 | l.max = nil 47 | 48 | } 49 | 50 | // Put inserts a new item into the list, ensuring that the list is sorted 51 | // after insertion. If an item with the same version already exists in the 52 | // list, then the value is updated. 53 | func (l *List) Put(ver uint64, val []byte) *Item { 54 | 55 | l.lock.Lock() 56 | defer l.lock.Unlock() 57 | 58 | // If there is no min or max for 59 | // this list, then we can just add 60 | // this item as the min and max. 61 | 62 | if l.min == nil && l.max == nil { 63 | i := &Item{ver: ver, val: val, list: l} 64 | l.min, l.max = i, i 65 | l.size++ 66 | return i 67 | } 68 | 69 | // 70 | // 71 | // 72 | 73 | i := &Item{ver: ver, val: val, list: l} 74 | 75 | // Otherwise find the nearest item 76 | // to this version so we can update 77 | // it or prepend / append to it. 78 | 79 | f := l.find(ver, Nearest) 80 | 81 | if f.ver == ver { 82 | if f.prev != nil { 83 | f.prev.next = i 84 | i.prev = f.prev 85 | } 86 | if f.next != nil { 87 | f.next.prev = i 88 | i.next = f.next 89 | } 90 | // f.val = val 91 | // return f 92 | } 93 | 94 | // If the found item version is not 95 | // the same version as the one we're 96 | // updating then insert the new item. 97 | 98 | if f.ver < ver { 99 | if f.next != nil { 100 | f.next.prev = i 101 | i.next = f.next 102 | f.next = i 103 | } 104 | i.prev = f 105 | f.next = i 106 | } 107 | 108 | if f.ver > ver { 109 | i.next = f 110 | f.prev = i 111 | } 112 | 113 | // If there are no previous items 114 | // before this item then mark this 115 | // item as the minimum in the list. 116 | 117 | if i.prev == nil { 118 | l.min = i 119 | } 120 | 121 | // If there are no subsequent items 122 | // after this item then mark this 123 | // item as the maximum in the list. 124 | 125 | if i.next == nil { 126 | l.max = i 127 | } 128 | 129 | // If this was an addition and not 130 | // and update to the list, then 131 | // increment the size of the list 132 | 133 | if f.ver != ver { 134 | l.size++ 135 | } 136 | 137 | return f 138 | 139 | } 140 | 141 | // Del deletes a specific item from the list, returning the previous item 142 | // if it existed. If it did not exist, a nil value is returned. 143 | func (l *List) Del(ver uint64, meth Find) *Item { 144 | 145 | l.lock.Lock() 146 | defer l.lock.Unlock() 147 | 148 | i := l.find(ver, meth) 149 | 150 | if i != nil { 151 | 152 | if i.prev != nil && i.next != nil { 153 | i.prev.next = i.next 154 | i.next.prev = i.prev 155 | i.prev = nil 156 | i.next = nil 157 | } else if i.prev != nil { 158 | i.prev.next = nil 159 | l.max = i.prev 160 | i.prev = nil 161 | } else if i.next != nil { 162 | i.next.prev = nil 163 | l.min = i.next 164 | i.next = nil 165 | } else { 166 | l.min = nil 167 | l.max = nil 168 | } 169 | 170 | i.list = nil 171 | 172 | l.size-- 173 | 174 | } 175 | 176 | return i 177 | 178 | } 179 | 180 | // Exp expunges all items in the list, upto and including the specified 181 | // version, returning the latest version, or a nil value if not found. 182 | func (l *List) Exp(ver uint64, meth Find) *Item { 183 | 184 | l.lock.Lock() 185 | defer l.lock.Unlock() 186 | 187 | i := l.find(ver, meth) 188 | 189 | if i != nil { 190 | 191 | for now := i; now != nil; now = now.prev { 192 | l.size-- 193 | } 194 | 195 | if i.next != nil { 196 | i.next.prev = nil 197 | l.min = i.next 198 | i.next = nil 199 | } 200 | 201 | } 202 | 203 | return i 204 | 205 | } 206 | 207 | // Get gets a specific item from the list. If the exact item does not 208 | // exist in the list, then a nil value is returned. 209 | func (l *List) Get(ver uint64, meth Find) *Item { 210 | 211 | l.lock.RLock() 212 | defer l.lock.RUnlock() 213 | 214 | return l.find(ver, meth) 215 | 216 | } 217 | 218 | // Len returns the total number of items in the list. 219 | func (l *List) Len() int { 220 | 221 | l.lock.RLock() 222 | defer l.lock.RUnlock() 223 | 224 | return l.size 225 | 226 | } 227 | 228 | // Min returns the first item in the list. In a time-series list this can be 229 | // used to get the initial version. 230 | func (l *List) Min() *Item { 231 | 232 | l.lock.RLock() 233 | defer l.lock.RUnlock() 234 | 235 | return l.min 236 | 237 | } 238 | 239 | // Max returns the last item in the list. In a time-series list this can be 240 | // used to get the latest version. 241 | func (l *List) Max() *Item { 242 | 243 | l.lock.RLock() 244 | defer l.lock.RUnlock() 245 | 246 | return l.max 247 | 248 | } 249 | 250 | // Walk iterates over the list starting at the first version, and continuing 251 | // until the walk function returns true. 252 | func (l *List) Walk(fn func(*Item) bool) { 253 | 254 | l.lock.RLock() 255 | defer l.lock.RUnlock() 256 | 257 | for i := l.min; i != nil && !fn(i); i = i.next { 258 | continue 259 | } 260 | 261 | } 262 | 263 | // Rng iterates over the list starting at the first version, and continuing 264 | // until the walk function returns true. 265 | func (l *List) Rng(beg, end uint64, fn func(*Item) bool) { 266 | 267 | l.lock.RLock() 268 | defer l.lock.RUnlock() 269 | 270 | for i := l.min; i != nil; i = i.next { 271 | if i.ver >= beg && i.ver < end && fn(i) { 272 | return 273 | } 274 | } 275 | 276 | } 277 | 278 | // --------------------------------------------------------------------------- 279 | 280 | func (l *List) find(ver uint64, what Find) (i *Item) { 281 | 282 | if l.min == nil && l.max == nil { 283 | return nil 284 | } 285 | 286 | switch what { 287 | 288 | case Prev: // Get the item below the specified version 289 | 290 | if i = l.find(ver, Upto); i != nil { 291 | return i.prev 292 | } 293 | 294 | case Next: // Get the item above the specified version 295 | 296 | if i = l.find(ver, Upto); i != nil { 297 | return i.next 298 | } 299 | 300 | case Upto: // Get the item up to the specified version 301 | 302 | for i = l.max; i != nil && i.ver > ver; i = i.prev { 303 | // Ignore 304 | } 305 | return i 306 | 307 | case Exact: // Get the exact specified version 308 | 309 | for i = l.max; i != nil && i.ver >= ver; i = i.prev { 310 | if i.ver == ver { 311 | return i 312 | } 313 | } 314 | 315 | case Nearest: // Get the item nearest the specified version 316 | 317 | for i = l.max; i != nil; i = i.prev { 318 | if i.ver <= ver || i.prev == nil { 319 | return i 320 | } 321 | } 322 | 323 | } 324 | 325 | return nil 326 | 327 | } 328 | -------------------------------------------------------------------------------- /data/list_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/smartystreets/goconvey/convey" 7 | ) 8 | 9 | var c int 10 | var x *List 11 | var i *Item 12 | 13 | func TestMain(t *testing.T) { 14 | 15 | x = new(List) 16 | 17 | Convey("Get with nothing in list", t, func() { 18 | So(x.Get(3, Exact), ShouldBeNil) 19 | }) 20 | 21 | Convey("Can set 2nd item", t, func() { 22 | x.Put(2, []byte{2}) 23 | i = x.Get(2, Exact) 24 | So(x.Min(), ShouldEqual, x.Get(2, Exact)) 25 | So(x.Max(), ShouldEqual, x.Get(2, Exact)) 26 | So(i.Ver(), ShouldEqual, 2) 27 | So(i.Val(), ShouldResemble, []byte{2}) 28 | So(i.Prev(), ShouldEqual, nil) 29 | So(i.Next(), ShouldEqual, nil) 30 | }) 31 | 32 | Convey("Can set 4th item", t, func() { 33 | x.Put(4, []byte{4}) 34 | i = x.Get(4, Exact) 35 | So(x.Min(), ShouldEqual, x.Get(2, Exact)) 36 | So(x.Max(), ShouldEqual, x.Get(4, Exact)) 37 | So(i.Ver(), ShouldEqual, 4) 38 | So(i.Val(), ShouldResemble, []byte{4}) 39 | So(i.Prev().Val(), ShouldResemble, []byte{2}) 40 | So(i.Next(), ShouldEqual, nil) 41 | }) 42 | 43 | Convey("Can set 1st item", t, func() { 44 | x.Put(1, []byte{1}) 45 | i = x.Get(1, Exact) 46 | So(x.Min(), ShouldEqual, x.Get(1, Exact)) 47 | So(x.Max(), ShouldEqual, x.Get(4, Exact)) 48 | So(i.Ver(), ShouldEqual, 1) 49 | So(i.Val(), ShouldResemble, []byte{1}) 50 | So(i.Prev(), ShouldEqual, nil) 51 | So(i.Next().Val(), ShouldResemble, []byte{2}) 52 | }) 53 | 54 | Convey("Can set 3rd item", t, func() { 55 | x.Put(3, []byte{3}) 56 | i = x.Get(3, Exact) 57 | So(x.Min(), ShouldEqual, x.Get(1, Exact)) 58 | So(x.Max(), ShouldEqual, x.Get(4, Exact)) 59 | So(i.Ver(), ShouldEqual, 3) 60 | So(i.Val(), ShouldResemble, []byte{3}) 61 | So(i.Prev().Val(), ShouldResemble, []byte{2}) 62 | So(i.Next().Val(), ShouldResemble, []byte{4}) 63 | }) 64 | 65 | Convey("Can set 5th item", t, func() { 66 | x.Put(5, []byte{5}) 67 | i = x.Get(5, Exact) 68 | So(x.Min(), ShouldEqual, x.Get(1, Exact)) 69 | So(x.Max(), ShouldEqual, x.Get(5, Exact)) 70 | So(i.Ver(), ShouldEqual, 5) 71 | So(i.Val(), ShouldResemble, []byte{5}) 72 | So(i.Prev().Val(), ShouldResemble, []byte{4}) 73 | So(i.Next(), ShouldEqual, nil) 74 | }) 75 | 76 | Convey("Can get list size", t, func() { 77 | So(x.Len(), ShouldEqual, 5) 78 | }) 79 | 80 | // ---------------------------------------- 81 | // ---------------------------------------- 82 | // ---------------------------------------- 83 | 84 | Convey("------------------------------", t, nil) 85 | 86 | Convey("Can get prev item to 1", t, func() { 87 | So(x.Get(1, Prev), ShouldBeNil) 88 | }) 89 | 90 | Convey("Can get prev item to 3", t, func() { 91 | i = x.Get(3, Prev) 92 | So(i.Ver(), ShouldEqual, 2) 93 | So(i.Val(), ShouldResemble, []byte{2}) 94 | }) 95 | 96 | Convey("Can get next item to 3", t, func() { 97 | i = x.Get(3, Next) 98 | So(i.Ver(), ShouldEqual, 4) 99 | So(i.Val(), ShouldResemble, []byte{4}) 100 | }) 101 | 102 | Convey("Can get next item to 5", t, func() { 103 | So(x.Get(5, Next), ShouldBeNil) 104 | }) 105 | 106 | // ---------------------------------------- 107 | // ---------------------------------------- 108 | // ---------------------------------------- 109 | 110 | Convey("------------------------------", t, nil) 111 | 112 | Convey("Can get upto item at 0", t, func() { 113 | So(x.Get(0, Upto), ShouldBeNil) 114 | }) 115 | 116 | Convey("Can get upto item at 1", t, func() { 117 | i = x.Get(1, Upto) 118 | So(i.Ver(), ShouldEqual, 1) 119 | So(i.Val(), ShouldResemble, []byte{1}) 120 | So(i, ShouldEqual, x.Get(1, Exact)) 121 | }) 122 | 123 | Convey("Can get upto item at 3", t, func() { 124 | i = x.Get(3, Upto) 125 | So(i.Ver(), ShouldEqual, 3) 126 | So(i.Val(), ShouldResemble, []byte{3}) 127 | So(i, ShouldEqual, x.Get(3, Exact)) 128 | }) 129 | 130 | Convey("Can get upto item at 5", t, func() { 131 | i = x.Get(5, Upto) 132 | So(i.Ver(), ShouldEqual, 5) 133 | So(i.Val(), ShouldResemble, []byte{5}) 134 | So(i, ShouldEqual, x.Get(5, Exact)) 135 | }) 136 | 137 | Convey("Can get upto item at 7", t, func() { 138 | i = x.Get(7, Upto) 139 | So(i.Ver(), ShouldEqual, 5) 140 | So(i.Val(), ShouldResemble, []byte{5}) 141 | So(i, ShouldEqual, x.Get(5, Exact)) 142 | }) 143 | 144 | // ---------------------------------------- 145 | // ---------------------------------------- 146 | // ---------------------------------------- 147 | 148 | Convey("------------------------------", t, nil) 149 | 150 | Convey("Can get minimum item", t, func() { 151 | i = x.Min() 152 | So(i.Ver(), ShouldEqual, 1) 153 | So(i.Val(), ShouldResemble, []byte{1}) 154 | }) 155 | 156 | Convey("Can get maximum item", t, func() { 157 | i = x.Max() 158 | So(i.Ver(), ShouldEqual, 5) 159 | So(i.Val(), ShouldResemble, []byte{5}) 160 | }) 161 | 162 | // ---------------------------------------- 163 | // ---------------------------------------- 164 | // ---------------------------------------- 165 | 166 | Convey("------------------------------", t, nil) 167 | 168 | Convey("Can get nearest item", t, func() { 169 | So(x.Get(0, Nearest).Ver(), ShouldEqual, 1) 170 | So(x.Get(1, Nearest).Ver(), ShouldEqual, 1) 171 | So(x.Get(3, Nearest).Ver(), ShouldEqual, 3) 172 | So(x.Get(5, Nearest).Ver(), ShouldEqual, 5) 173 | So(x.Get(10, Nearest).Ver(), ShouldEqual, 5) 174 | }) 175 | 176 | // ---------------------------------------- 177 | // ---------------------------------------- 178 | // ---------------------------------------- 179 | 180 | Convey("------------------------------", t, nil) 181 | 182 | Convey("Can get range of items", t, func() { 183 | var items []*Item 184 | x.Rng(3, 5, func(i *Item) bool { 185 | items = append(items, i) 186 | return false 187 | }) 188 | So(len(items), ShouldEqual, 2) 189 | So(items[0], ShouldEqual, x.Get(3, Exact)) 190 | So(items[1], ShouldEqual, x.Get(4, Exact)) 191 | }) 192 | 193 | // ---------------------------------------- 194 | // ---------------------------------------- 195 | // ---------------------------------------- 196 | 197 | Convey("------------------------------", t, nil) 198 | 199 | Convey("Can delete 1st item", t, func() { 200 | i = x.Del(1, Exact) 201 | So(i.Ver(), ShouldEqual, 1) 202 | So(i.Val(), ShouldResemble, []byte{1}) 203 | So(i.Prev(), ShouldEqual, nil) 204 | So(i.Next(), ShouldEqual, nil) 205 | }) 206 | 207 | Convey("Can not get deleted item", t, func() { 208 | i = x.Get(1, Exact) 209 | So(i, ShouldBeNil) 210 | }) 211 | 212 | Convey("Can get minimum item", t, func() { 213 | i = x.Min() 214 | So(i.Ver(), ShouldEqual, 2) 215 | So(i.Val(), ShouldResemble, []byte{2}) 216 | So(i, ShouldEqual, x.Get(2, Exact)) 217 | }) 218 | 219 | Convey("Can get maximum item", t, func() { 220 | i = x.Max() 221 | So(i.Ver(), ShouldEqual, 5) 222 | So(i.Val(), ShouldResemble, []byte{5}) 223 | So(i, ShouldEqual, x.Get(5, Exact)) 224 | }) 225 | 226 | Convey("Can get list size", t, func() { 227 | So(x.Len(), ShouldEqual, 4) 228 | }) 229 | 230 | // ---------------------------------------- 231 | // ---------------------------------------- 232 | // ---------------------------------------- 233 | 234 | Convey("------------------------------", t, nil) 235 | 236 | Convey("Can delete 5th item", t, func() { 237 | i = x.Del(5, Exact) 238 | So(i.Ver(), ShouldEqual, 5) 239 | So(i.Val(), ShouldResemble, []byte{5}) 240 | So(i.Prev(), ShouldEqual, nil) 241 | So(i.Next(), ShouldEqual, nil) 242 | }) 243 | 244 | Convey("Can not get deleted item", t, func() { 245 | i = x.Get(5, Exact) 246 | So(i, ShouldBeNil) 247 | }) 248 | 249 | Convey("Can get minimum item", t, func() { 250 | i = x.Min() 251 | So(i.Ver(), ShouldEqual, 2) 252 | So(i.Val(), ShouldResemble, []byte{2}) 253 | So(i, ShouldEqual, x.Get(2, Exact)) 254 | }) 255 | 256 | Convey("Can get maximum item", t, func() { 257 | i = x.Max() 258 | So(i.Ver(), ShouldEqual, 4) 259 | So(i.Val(), ShouldResemble, []byte{4}) 260 | So(i, ShouldEqual, x.Get(4, Exact)) 261 | }) 262 | 263 | Convey("Can get list size", t, func() { 264 | So(x.Len(), ShouldEqual, 3) 265 | }) 266 | 267 | // ---------------------------------------- 268 | // ---------------------------------------- 269 | // ---------------------------------------- 270 | 271 | Convey("------------------------------", t, nil) 272 | 273 | Convey("Can delete 3rd item", t, func() { 274 | i = x.Del(3, Exact) 275 | So(i.Ver(), ShouldEqual, 3) 276 | So(i.Val(), ShouldResemble, []byte{3}) 277 | So(i.Prev(), ShouldEqual, nil) 278 | So(i.Next(), ShouldEqual, nil) 279 | }) 280 | 281 | Convey("Can not get deleted item", t, func() { 282 | i = x.Get(3, Exact) 283 | So(i, ShouldBeNil) 284 | }) 285 | 286 | Convey("Can get minimum item", t, func() { 287 | i = x.Min() 288 | So(i.Ver(), ShouldEqual, 2) 289 | So(i.Val(), ShouldResemble, []byte{2}) 290 | }) 291 | 292 | Convey("Can get maximum item", t, func() { 293 | i = x.Max() 294 | So(i.Ver(), ShouldEqual, 4) 295 | So(i.Val(), ShouldResemble, []byte{4}) 296 | }) 297 | 298 | Convey("Can get list size", t, func() { 299 | So(x.Len(), ShouldEqual, 2) 300 | }) 301 | 302 | // ---------------------------------------- 303 | // ---------------------------------------- 304 | // ---------------------------------------- 305 | 306 | Convey("------------------------------", t, nil) 307 | 308 | Convey("Can delete 2nd item", t, func() { 309 | i = x.Del(2, Exact) 310 | So(i.Ver(), ShouldEqual, 2) 311 | So(i.Val(), ShouldResemble, []byte{2}) 312 | So(i.Prev(), ShouldEqual, nil) 313 | So(i.Next(), ShouldEqual, nil) 314 | }) 315 | 316 | Convey("Can not get deleted item", t, func() { 317 | i = x.Get(2, Exact) 318 | So(i, ShouldBeNil) 319 | }) 320 | 321 | Convey("Can get minimum item", t, func() { 322 | i = x.Min() 323 | So(i.Ver(), ShouldEqual, 4) 324 | So(i.Val(), ShouldResemble, []byte{4}) 325 | }) 326 | 327 | Convey("Can get maximum item", t, func() { 328 | i = x.Max() 329 | So(i.Ver(), ShouldEqual, 4) 330 | So(i.Val(), ShouldResemble, []byte{4}) 331 | }) 332 | 333 | Convey("Can get list size", t, func() { 334 | So(x.Len(), ShouldEqual, 1) 335 | }) 336 | 337 | // ---------------------------------------- 338 | // ---------------------------------------- 339 | // ---------------------------------------- 340 | 341 | Convey("------------------------------", t, nil) 342 | 343 | Convey("Can delete 4th item", t, func() { 344 | i = x.Del(4, Exact) 345 | So(i.Ver(), ShouldEqual, 4) 346 | So(i.Val(), ShouldResemble, []byte{4}) 347 | So(i.Prev(), ShouldEqual, nil) 348 | So(i.Next(), ShouldEqual, nil) 349 | }) 350 | 351 | Convey("Can not get deleted item", t, func() { 352 | i = x.Get(4, Exact) 353 | So(i, ShouldBeNil) 354 | }) 355 | 356 | Convey("Can get minimum item", t, func() { 357 | i = x.Min() 358 | So(i, ShouldBeNil) 359 | }) 360 | 361 | Convey("Can get maximum item", t, func() { 362 | i = x.Max() 363 | So(i, ShouldBeNil) 364 | }) 365 | 366 | Convey("Can get list size", t, func() { 367 | So(x.Len(), ShouldEqual, 0) 368 | }) 369 | 370 | // ---------------------------------------- 371 | // ---------------------------------------- 372 | // ---------------------------------------- 373 | 374 | Convey("------------------------------", t, nil) 375 | 376 | Convey("Can set 2nd item", t, func() { 377 | x.Put(2, []byte{2}) 378 | i = x.Get(2, Exact) 379 | So(x.Min(), ShouldEqual, x.Get(2, Exact)) 380 | So(x.Max(), ShouldEqual, x.Get(2, Exact)) 381 | So(i.Ver(), ShouldEqual, 2) 382 | So(i.Val(), ShouldResemble, []byte{2}) 383 | }) 384 | 385 | Convey("Can set 4th item", t, func() { 386 | x.Put(4, []byte{4}) 387 | i = x.Get(4, Exact) 388 | So(x.Min(), ShouldEqual, x.Get(2, Exact)) 389 | So(x.Max(), ShouldEqual, x.Get(4, Exact)) 390 | So(i.Ver(), ShouldEqual, 4) 391 | So(i.Val(), ShouldResemble, []byte{4}) 392 | }) 393 | 394 | Convey("Can replace 2nd item", t, func() { 395 | i = x.Put(2, []byte{'R'}) 396 | So(i.Ver(), ShouldEqual, 2) 397 | So(i.Val(), ShouldResemble, []byte{2}) 398 | i = x.Get(2, Exact) 399 | So(i.Ver(), ShouldEqual, 2) 400 | So(i.Val(), ShouldResemble, []byte{'R'}) 401 | }) 402 | 403 | Convey("Can replace 4th item", t, func() { 404 | i = x.Put(4, []byte{'R'}) 405 | So(i.Ver(), ShouldEqual, 4) 406 | So(i.Val(), ShouldResemble, []byte{4}) 407 | i = x.Get(4, Exact) 408 | So(i.Ver(), ShouldEqual, 4) 409 | So(i.Val(), ShouldResemble, []byte{'R'}) 410 | }) 411 | 412 | Convey("Can get list size", t, func() { 413 | So(x.Len(), ShouldEqual, 2) 414 | }) 415 | 416 | // ---------------------------------------- 417 | // ---------------------------------------- 418 | // ---------------------------------------- 419 | 420 | Convey("------------------------------", t, nil) 421 | 422 | Convey("Can reset 2nd item", t, func() { 423 | i = x.Get(2, Exact) 424 | i.Set([]byte{'T'}) 425 | So(i.Val(), ShouldResemble, []byte{'T'}) 426 | So(x.Get(2, Exact).Val(), ShouldResemble, []byte{'T'}) 427 | }) 428 | 429 | Convey("Can reset 4th item", t, func() { 430 | i = x.Get(4, Exact) 431 | i.Set([]byte{'F'}) 432 | So(i.Val(), ShouldResemble, []byte{'F'}) 433 | So(x.Get(4, Exact).Val(), ShouldResemble, []byte{'F'}) 434 | }) 435 | 436 | // ---------------------------------------- 437 | // ---------------------------------------- 438 | // ---------------------------------------- 439 | 440 | Convey("------------------------------", t, nil) 441 | 442 | Convey("Can walk through the list and exit", t, func() { 443 | var items []*Item 444 | x.Walk(func(i *Item) bool { 445 | items = append(items, i) 446 | return true 447 | }) 448 | So(len(items), ShouldEqual, 1) 449 | So(items[0], ShouldEqual, x.Get(2, Exact)) 450 | }) 451 | 452 | Convey("Can walk through the list without exiting", t, func() { 453 | var items []*Item 454 | x.Walk(func(i *Item) bool { 455 | items = append(items, i) 456 | return false 457 | }) 458 | So(len(items), ShouldEqual, 2) 459 | So(items[0], ShouldEqual, x.Get(2, Exact)) 460 | So(items[1], ShouldEqual, x.Get(4, Exact)) 461 | }) 462 | 463 | // ---------------------------------------- 464 | // ---------------------------------------- 465 | // ---------------------------------------- 466 | 467 | Convey("------------------------------", t, nil) 468 | 469 | Convey("Can insert some items", t, func() { 470 | x.Put(1, []byte{1}) 471 | x.Put(2, []byte{2}) 472 | x.Put(3, []byte{3}) 473 | x.Put(4, []byte{4}) 474 | x.Put(5, []byte{5}) 475 | So(x.Len(), ShouldEqual, 5) 476 | }) 477 | 478 | Convey("Can expire upto 3rd item", t, func() { 479 | i = x.Exp(3, Exact) 480 | So(x.Len(), ShouldEqual, 2) 481 | So(i.Ver(), ShouldEqual, 3) 482 | So(i.Val(), ShouldResemble, []byte{3}) 483 | }) 484 | 485 | // ---------------------------------------- 486 | // ---------------------------------------- 487 | // ---------------------------------------- 488 | 489 | Convey("------------------------------", t, nil) 490 | 491 | Convey("Can clear the list", t, func() { 492 | x.Clr() 493 | So(x.Len(), ShouldEqual, 0) 494 | }) 495 | 496 | // ---------------------------------------- 497 | // ---------------------------------------- 498 | // ---------------------------------------- 499 | 500 | Convey("------------------------------", t, nil) 501 | 502 | Convey("Can insert some items", t, func() { 503 | x.Put(1, []byte{1}) 504 | x.Put(2, []byte{2}) 505 | x.Put(3, []byte{3}) 506 | x.Put(4, []byte{4}) 507 | x.Put(5, []byte{5}) 508 | So(x.Len(), ShouldEqual, 5) 509 | }) 510 | 511 | Convey("Can self delete 3rd item", t, func() { 512 | i = x.Get(3, Exact) 513 | So(i.Del(), ShouldEqual, i) 514 | So(x.Len(), ShouldEqual, 4) 515 | }) 516 | 517 | Convey("Can self delete 1st item", t, func() { 518 | i = x.Get(1, Exact) 519 | So(i.Del(), ShouldEqual, i) 520 | So(x.Len(), ShouldEqual, 3) 521 | }) 522 | 523 | Convey("Can self delete 5th item", t, func() { 524 | i = x.Get(5, Exact) 525 | So(i.Del(), ShouldEqual, i) 526 | So(x.Len(), ShouldEqual, 2) 527 | }) 528 | 529 | Convey("Can self delete 2nd item", t, func() { 530 | i = x.Get(2, Exact) 531 | So(i.Del(), ShouldEqual, i) 532 | So(x.Len(), ShouldEqual, 1) 533 | }) 534 | 535 | Convey("Can self delete 4th item", t, func() { 536 | i = x.Get(4, Exact) 537 | So(i.Del(), ShouldEqual, i) 538 | So(x.Len(), ShouldEqual, 0) 539 | }) 540 | 541 | } 542 | -------------------------------------------------------------------------------- /data/node.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "bytes" 5 | "sort" 6 | ) 7 | 8 | // Node represents an immutable node in the radix tree which 9 | // can be either an edge node or a leaf node. 10 | type Node struct { 11 | leaf *leaf 12 | edges []*Node 13 | prefix []byte 14 | } 15 | 16 | type leaf struct { 17 | key []byte 18 | val *List 19 | } 20 | 21 | // Min returns the key and value of the minimum item in the 22 | // subtree of the current node. 23 | func (n *Node) Min() ([]byte, *List) { 24 | 25 | for { 26 | 27 | if n.isLeaf() { 28 | return n.leaf.key, n.leaf.val 29 | } 30 | 31 | if len(n.edges) > 0 { 32 | n = n.edges[0] 33 | } else { 34 | break 35 | } 36 | 37 | } 38 | 39 | return nil, nil 40 | 41 | } 42 | 43 | // Max returns the key and value of the maximum item in the 44 | // subtree of the current node. 45 | func (n *Node) Max() ([]byte, *List) { 46 | 47 | for { 48 | 49 | if num := len(n.edges); num > 0 { 50 | n = n.edges[num-1] 51 | continue 52 | } 53 | 54 | if n.isLeaf() { 55 | return n.leaf.key, n.leaf.val 56 | } 57 | 58 | break 59 | 60 | } 61 | 62 | return nil, nil 63 | 64 | } 65 | 66 | // Path is used to recurse over the tree only visiting nodes 67 | // which are above this node in the tree. 68 | func (n *Node) Path(k []byte, f Walker) { 69 | 70 | s := k 71 | 72 | for { 73 | 74 | if n.leaf != nil { 75 | if f(n.leaf.key, n.leaf.val) { 76 | return 77 | } 78 | } 79 | 80 | if len(s) == 0 { 81 | return 82 | } 83 | 84 | if _, n = n.getSub(s[0]); n == nil { 85 | return 86 | } 87 | 88 | if bytes.HasPrefix(s, n.prefix) { 89 | s = s[len(n.prefix):] 90 | } else { 91 | break 92 | } 93 | 94 | } 95 | 96 | } 97 | 98 | // Subs is used to recurse over the tree only visiting nodes 99 | // which are directly under this node in the tree. 100 | func (n *Node) Subs(k []byte, f Walker) { 101 | 102 | s := k 103 | 104 | for { 105 | 106 | // Check for key exhaution 107 | if len(s) == 0 { 108 | subs(n, f, false) 109 | return 110 | } 111 | 112 | // Look for an edge 113 | if _, n = n.getSub(s[0]); n == nil { 114 | break 115 | } 116 | 117 | // Consume the search prefix 118 | if bytes.HasPrefix(s, n.prefix) { 119 | s = s[len(n.prefix):] 120 | } else if bytes.HasPrefix(n.prefix, s) { 121 | subs(n, f, true) 122 | return 123 | } else { 124 | break 125 | } 126 | 127 | } 128 | 129 | } 130 | 131 | // Walk is used to recurse over the tree only visiting nodes 132 | // which are under this node in the tree. 133 | func (n *Node) Walk(k []byte, f Walker) { 134 | 135 | s := k 136 | 137 | for { 138 | 139 | // Check for key exhaution 140 | if len(s) == 0 { 141 | walk(n, f, false) 142 | return 143 | } 144 | 145 | // Look for an edge 146 | if _, n = n.getSub(s[0]); n == nil { 147 | break 148 | } 149 | 150 | // Consume the search prefix 151 | if bytes.HasPrefix(s, n.prefix) { 152 | s = s[len(n.prefix):] 153 | } else if bytes.HasPrefix(n.prefix, s) { 154 | walk(n, f, false) 155 | return 156 | } else { 157 | break 158 | } 159 | 160 | } 161 | 162 | } 163 | 164 | // ------------------------------ 165 | // ------------------------------ 166 | // ------------------------------ 167 | // ------------------------------ 168 | // ------------------------------ 169 | 170 | func (n *Node) isLeaf() bool { 171 | return n.leaf != nil 172 | } 173 | 174 | func (n *Node) dup() *Node { 175 | d := &Node{} 176 | if n.leaf != nil { 177 | d.leaf = &leaf{} 178 | *d.leaf = *n.leaf 179 | } 180 | if n.prefix != nil { 181 | d.prefix = make([]byte, len(n.prefix)) 182 | copy(d.prefix, n.prefix) 183 | } 184 | if len(n.edges) != 0 { 185 | d.edges = make([]*Node, len(n.edges)) 186 | copy(d.edges, n.edges) 187 | } 188 | return d 189 | } 190 | 191 | func (n *Node) addSub(s *Node) { 192 | num := len(n.edges) 193 | idx := sort.Search(num, func(i int) bool { 194 | return n.edges[i].prefix[0] >= s.prefix[0] 195 | }) 196 | n.edges = append(n.edges, s) 197 | if idx != num { 198 | copy(n.edges[idx+1:], n.edges[idx:num]) 199 | n.edges[idx] = s 200 | } 201 | } 202 | 203 | func (n *Node) repSub(s *Node) { 204 | num := len(n.edges) 205 | idx := sort.Search(num, func(i int) bool { 206 | return n.edges[i].prefix[0] >= s.prefix[0] 207 | }) 208 | if idx < num && n.edges[idx].prefix[0] == s.prefix[0] { 209 | n.edges[idx] = s 210 | return 211 | } 212 | panic("replacing missing edge") 213 | } 214 | 215 | func (n *Node) getSub(label byte) (int, *Node) { 216 | num := len(n.edges) 217 | idx := sort.Search(num, func(i int) bool { 218 | return n.edges[i].prefix[0] >= label 219 | }) 220 | if idx < num && n.edges[idx].prefix[0] == label { 221 | return idx, n.edges[idx] 222 | } 223 | return -1, nil 224 | } 225 | 226 | func (n *Node) delSub(label byte) { 227 | num := len(n.edges) 228 | idx := sort.Search(num, func(i int) bool { 229 | return n.edges[i].prefix[0] >= label 230 | }) 231 | if idx < num && n.edges[idx].prefix[0] == label { 232 | copy(n.edges[idx:], n.edges[idx+1:]) 233 | n.edges[len(n.edges)-1] = nil 234 | n.edges = n.edges[:len(n.edges)-1] 235 | } 236 | } 237 | 238 | func (n *Node) mergeChild() { 239 | e := n.edges[0] 240 | child := e 241 | n.prefix = concat(n.prefix, child.prefix) 242 | if child.leaf != nil { 243 | n.leaf = new(leaf) 244 | *n.leaf = *child.leaf 245 | } else { 246 | n.leaf = nil 247 | } 248 | if len(child.edges) != 0 { 249 | n.edges = make([]*Node, len(child.edges)) 250 | copy(n.edges, child.edges) 251 | } else { 252 | n.edges = nil 253 | } 254 | } 255 | 256 | func subs(n *Node, f Walker, sub bool) bool { 257 | 258 | // Visit the leaf values if any 259 | if sub && n.leaf != nil { 260 | if f(n.leaf.key, n.leaf.val) { 261 | return true 262 | } 263 | return false 264 | } 265 | 266 | // Recurse on the children 267 | for _, e := range n.edges { 268 | if subs(e, f, true) { 269 | return true 270 | } 271 | } 272 | 273 | return false 274 | 275 | } 276 | 277 | func walk(n *Node, f Walker, sub bool) bool { 278 | 279 | // Visit the leaf values if any 280 | if n.leaf != nil { 281 | if f(n.leaf.key, n.leaf.val) { 282 | return true 283 | } 284 | } 285 | 286 | // Recurse on the children 287 | for _, e := range n.edges { 288 | if walk(e, f, true) { 289 | return true 290 | } 291 | } 292 | 293 | return false 294 | 295 | } 296 | 297 | func (n *Node) get(k []byte) *List { 298 | 299 | s := k 300 | 301 | for { 302 | 303 | // Check for key exhaution 304 | if len(s) == 0 { 305 | if n.isLeaf() { 306 | return n.leaf.val 307 | } 308 | break 309 | } 310 | 311 | // Look for an edge 312 | _, n = n.getSub(s[0]) 313 | if n == nil { 314 | break 315 | } 316 | 317 | // Consume the search prefix 318 | if bytes.HasPrefix(s, n.prefix) { 319 | s = s[len(n.prefix):] 320 | } else { 321 | break 322 | } 323 | 324 | } 325 | 326 | return nil 327 | 328 | } 329 | -------------------------------------------------------------------------------- /data/tree.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | // Tree represents an immutable versioned radix tree. 4 | type Tree struct { 5 | size int 6 | root *Node 7 | } 8 | 9 | // New returns an empty Tree 10 | func New() *Tree { 11 | return &Tree{root: &Node{}} 12 | } 13 | 14 | // Size is used to return the number of elements in the tree. 15 | func (t *Tree) Size() int { 16 | return t.size 17 | } 18 | 19 | // Copy starts a new transaction that can be used to mutate the tree 20 | func (t *Tree) Copy() *Copy { 21 | return &Copy{size: t.size, root: t.root} 22 | } 23 | 24 | // Walker represents a callback function which is to be used when 25 | // iterating through the tree using Path, Subs, or Walk. It will be 26 | // populated with the key and list of the current item, and returns 27 | // a bool signifying if the iteration should be terminated. 28 | type Walker func(key []byte, val *List) (exit bool) 29 | -------------------------------------------------------------------------------- /data/tree_test.go: -------------------------------------------------------------------------------- 1 | package data 2 | 3 | import ( 4 | "fmt" 5 | "math" 6 | "testing" 7 | 8 | . "github.com/smartystreets/goconvey/convey" 9 | ) 10 | 11 | var m uint64 = math.MaxUint64 12 | 13 | var s = []string{ 14 | "/some", // 0 15 | "/test", // 1 16 | "/test/one", // 2 17 | "/test/one/sub-one", // 3 18 | "/test/one/sub-one/1st", // 4 19 | "/test/one/sub-one/2nd", // 5 20 | "/test/one/sub-two", // 6 21 | "/test/one/sub-two/1st", // 7 22 | "/test/one/sub-two/2nd", // 8 23 | "/test/one/sub-zen", // 9 24 | "/test/one/sub-zen/1st", // 10 ---------- 25 | "/test/one/sub-zen/2nd", // 11 26 | "/test/two", // 12 27 | "/test/two/sub-one", // 13 28 | "/test/two/sub-one/1st", // 14 29 | "/test/two/sub-one/2nd", // 15 30 | "/test/two/sub-two", // 16 31 | "/test/two/sub-two/1st", // 17 32 | "/test/two/sub-two/2nd", // 18 33 | "/test/two/sub-zen", // 19 34 | "/test/two/sub-zen/1st", // 20 35 | "/test/two/sub-zen/2nd", // 21 36 | "/test/zen", // 22 37 | "/test/zen/sub-one", // 23 38 | "/test/zen/sub-one/1st", // 24 39 | "/test/zen/sub-one/2nd", // 25 40 | "/test/zen/sub-two", // 26 41 | "/test/zen/sub-two/1st", // 27 42 | "/test/zen/sub-two/2nd", // 28 43 | "/test/zen/sub-zen", // 29 44 | "/test/zen/sub-zen/1st", // 30 45 | "/test/zen/sub-zen/2nd", // 31 46 | "/zoo", // 32 47 | "/zoo/some", // 33 48 | "/zoo/some/path", // 34 49 | } 50 | 51 | var p = [][]int{ 52 | {0, 0}, // 0 53 | {0, 1}, // 1 54 | {0, 1, 0, 0}, // 2 55 | {0, 1, 0, 0, 0, 0}, // 3 56 | {0, 1, 0, 0, 0, 0, 0, 0}, // 4 57 | {0, 1, 0, 0, 0, 0, 0, 1}, // 5 58 | {0, 1, 0, 0, 0, 1}, // 6 59 | {0, 1, 0, 0, 0, 1, 0, 0}, // 7 60 | {0, 1, 0, 0, 0, 1, 0, 1}, // 8 61 | {0, 1, 0, 0, 0, 2}, // 9 62 | {0, 1, 0, 0, 0, 2, 0, 0}, // 10 ---------- 63 | {0, 1, 0, 0, 0, 2, 0, 1}, // 11 64 | {0, 1, 0, 1}, // 12 65 | {0, 1, 0, 1, 0, 0}, // 13 66 | {0, 1, 0, 1, 0, 0, 0, 0}, // 14 67 | {0, 1, 0, 1, 0, 0, 0, 1}, // 15 68 | {0, 1, 0, 1, 0, 1}, // 16 69 | {0, 1, 0, 1, 0, 1, 0, 0}, // 17 70 | {0, 1, 0, 1, 0, 1, 0, 1}, // 18 71 | {0, 1, 0, 1, 0, 2}, // 19 72 | {0, 1, 0, 1, 0, 2, 0, 0}, // 20 73 | {0, 1, 0, 1, 0, 2, 0, 1}, // 21 74 | {0, 1, 0, 2}, // 22 75 | {0, 1, 0, 2, 0, 0}, // 23 76 | {0, 1, 0, 2, 0, 0, 0, 0}, // 24 77 | {0, 1, 0, 2, 0, 0, 0, 1}, // 25 78 | {0, 1, 0, 2, 0, 1}, // 26 79 | {0, 1, 0, 2, 0, 1, 0, 0}, // 27 80 | {0, 1, 0, 2, 0, 1, 0, 1}, // 28 81 | {0, 1, 0, 2, 0, 2}, // 29 82 | {0, 1, 0, 2, 0, 2, 0, 0}, // 30 83 | {0, 1, 0, 2, 0, 2, 0, 1}, // 31 84 | {0, 2}, // 32 85 | {0, 2, 0}, // 33 86 | {0, 2, 0, 0}, // 34 87 | } 88 | 89 | func TestBasic(t *testing.T) { 90 | 91 | p := New() 92 | 93 | c := p.Copy() 94 | 95 | Convey("Get initial size", t, func() { 96 | So(p.Size(), ShouldEqual, 0) 97 | }) 98 | 99 | Convey("Can insert 1st item", t, func() { 100 | val := c.Put(0, []byte("/foo"), []byte("FOO")) 101 | So(val, ShouldBeNil) 102 | So(c.Size(), ShouldEqual, 1) 103 | So(c.Get(0, []byte("/foo")).Val(), ShouldResemble, []byte("FOO")) 104 | }) 105 | 106 | Convey("Can insert 2nd item", t, func() { 107 | val := c.Put(0, []byte("/bar"), []byte("BAR")) 108 | So(val, ShouldBeNil) 109 | So(c.Size(), ShouldEqual, 2) 110 | So(c.Get(0, []byte("/bar")).Val(), ShouldResemble, []byte("BAR")) 111 | }) 112 | 113 | Convey("Can get nil item", t, func() { 114 | val := c.Get(0, []byte("/")) 115 | So(val, ShouldEqual, nil) 116 | }) 117 | 118 | Convey("Can delete nil item", t, func() { 119 | val := c.Del(0, []byte("/foobar")) 120 | So(val, ShouldEqual, nil) 121 | So(c.Size(), ShouldEqual, 2) 122 | So(c.Get(0, []byte("/foobar")), ShouldEqual, nil) 123 | }) 124 | 125 | Convey("Can delete 1st item", t, func() { 126 | val := c.Cut([]byte("/foo")) 127 | So(val.Val(), ShouldResemble, []byte("FOO")) 128 | So(c.Size(), ShouldEqual, 1) 129 | So(c.Get(0, []byte("/foo")), ShouldEqual, nil) 130 | }) 131 | 132 | Convey("Can delete 2nd item", t, func() { 133 | val := c.Cut([]byte("/bar")) 134 | So(val.Val(), ShouldResemble, []byte("BAR")) 135 | So(c.Size(), ShouldEqual, 0) 136 | So(c.Get(0, []byte("/bar")), ShouldEqual, nil) 137 | }) 138 | 139 | Convey("Can commit transaction", t, func() { 140 | n := c.Tree() 141 | So(n, ShouldNotBeNil) 142 | So(n.Size(), ShouldEqual, 0) 143 | }) 144 | 145 | } 146 | 147 | func TestComplex(t *testing.T) { 148 | 149 | p := New() 150 | c := p.Copy() 151 | 152 | Convey("Can get empty `min`", t, func() { 153 | r := c.Root() 154 | k, v := r.Min() 155 | So(k, ShouldBeNil) 156 | So(v, ShouldBeNil) 157 | }) 158 | 159 | Convey("Can get empty `max`", t, func() { 160 | r := c.Root() 161 | k, v := r.Max() 162 | So(k, ShouldBeNil) 163 | So(v, ShouldBeNil) 164 | }) 165 | 166 | Convey("Can insert tree items", t, func() { 167 | for _, v := range s { 168 | c.Put(0, []byte(v), []byte(v)) 169 | } 170 | So(c.Size(), ShouldEqual, 35) 171 | for i := len(s) - 1; i > 0; i-- { 172 | c.Put(0, []byte(s[i]), []byte(s[i])) 173 | } 174 | So(c.Size(), ShouldEqual, 35) 175 | }) 176 | 177 | Convey("Can get proper `min`", t, func() { 178 | k, v := c.Root().Min() 179 | So(k, ShouldResemble, []byte("/some")) 180 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte("/some")) 181 | }) 182 | 183 | Convey("Can get proper `max`", t, func() { 184 | k, v := c.Root().Max() 185 | So(k, ShouldResemble, []byte("/zoo/some/path")) 186 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte("/zoo/some/path")) 187 | }) 188 | 189 | // ------------------------------------------------------------ 190 | 191 | Convey("Can iterate tree items at `nil` with `walk`", t, func() { 192 | i := 0 193 | c.Root().Walk(nil, func(k []byte, v *List) (e bool) { 194 | i++ 195 | return 196 | }) 197 | So(i, ShouldEqual, 35) 198 | }) 199 | 200 | Convey("Can iterate tree items at `/test/zen/s` with `walk`", t, func() { 201 | i := 0 202 | c.Root().Walk([]byte("/test/zen/s"), func(k []byte, v *List) (e bool) { 203 | i++ 204 | return 205 | }) 206 | So(i, ShouldEqual, 9) 207 | }) 208 | 209 | Convey("Can iterate tree items at `/test/zen/sub` with `walk`", t, func() { 210 | i := 0 211 | c.Root().Walk([]byte("/test/zen/sub"), func(k []byte, v *List) (e bool) { 212 | i++ 213 | return 214 | }) 215 | So(i, ShouldEqual, 9) 216 | }) 217 | 218 | Convey("Can iterate tree items at `/test/zen/sub-o` with `walk`", t, func() { 219 | i := 0 220 | c.Root().Walk([]byte("/test/zen/sub-o"), func(k []byte, v *List) (e bool) { 221 | i++ 222 | return 223 | }) 224 | So(i, ShouldEqual, 3) 225 | }) 226 | 227 | Convey("Can iterate tree items at `/test/zen/sub-one` with `walk`", t, func() { 228 | i := 0 229 | c.Root().Walk([]byte("/test/zen/sub-one"), func(k []byte, v *List) (e bool) { 230 | i++ 231 | return 232 | }) 233 | So(i, ShouldEqual, 3) 234 | }) 235 | 236 | Convey("Can iterate tree items at `/test/zen/sub` with `walk` and exit", t, func() { 237 | i := 0 238 | c.Root().Walk([]byte("/test/zen/sub"), func(k []byte, v *List) (e bool) { 239 | i++ 240 | return true 241 | }) 242 | So(i, ShouldEqual, 1) 243 | }) 244 | 245 | // ------------------------------------------------------------ 246 | 247 | Convey("Can iterate tree items at `/test/` with `subs`", t, func() { 248 | i := 0 249 | c.Root().Subs([]byte("/test/"), func(k []byte, v *List) (e bool) { 250 | i++ 251 | return 252 | }) 253 | So(i, ShouldEqual, 3) 254 | }) 255 | 256 | Convey("Can iterate tree items at `/test/zen/s` with `subs`", t, func() { 257 | i := 0 258 | c.Root().Subs([]byte("/test/zen/s"), func(k []byte, v *List) (e bool) { 259 | i++ 260 | return 261 | }) 262 | So(i, ShouldEqual, 3) 263 | }) 264 | 265 | Convey("Can iterate tree items at `/test/zen/sub` with `subs`", t, func() { 266 | i := 0 267 | c.Root().Subs([]byte("/test/zen/sub"), func(k []byte, v *List) (e bool) { 268 | i++ 269 | return 270 | }) 271 | So(i, ShouldEqual, 3) 272 | }) 273 | 274 | Convey("Can iterate tree items at `/test/zen/sub-o` with `subs`", t, func() { 275 | i := 0 276 | c.Root().Subs([]byte("/test/zen/sub-t"), func(k []byte, v *List) (e bool) { 277 | i++ 278 | return 279 | }) 280 | So(i, ShouldEqual, 1) 281 | }) 282 | 283 | Convey("Can iterate tree items at `/test/zen/sub-one` with `subs`", t, func() { 284 | i := 0 285 | c.Root().Subs([]byte("/test/zen/sub-one"), func(k []byte, v *List) (e bool) { 286 | i++ 287 | return 288 | }) 289 | So(i, ShouldEqual, 2) 290 | }) 291 | 292 | Convey("Can iterate tree items at `/test/zen/sub` with `subs` and exit", t, func() { 293 | i := 0 294 | c.Root().Subs([]byte("/test/zen/sub"), func(k []byte, v *List) (e bool) { 295 | i++ 296 | return true 297 | }) 298 | So(i, ShouldEqual, 1) 299 | }) 300 | 301 | // ------------------------------------------------------------ 302 | 303 | Convey("Can iterate tree items at `nil` with `path`", t, func() { 304 | i := 0 305 | c.Root().Path(nil, func(k []byte, v *List) (e bool) { 306 | i++ 307 | return 308 | }) 309 | So(i, ShouldEqual, 0) 310 | }) 311 | 312 | Convey("Can iterate tree items at `/test/zen/s` with `path`", t, func() { 313 | i := 0 314 | c.Root().Path([]byte("/test/zen/s"), func(k []byte, v *List) (e bool) { 315 | i++ 316 | return 317 | }) 318 | So(i, ShouldEqual, 2) 319 | }) 320 | 321 | Convey("Can iterate tree items at `/test/zen/sub` with `path`", t, func() { 322 | i := 0 323 | c.Root().Path([]byte("/test/zen/sub"), func(k []byte, v *List) (e bool) { 324 | i++ 325 | return 326 | }) 327 | So(i, ShouldEqual, 2) 328 | }) 329 | 330 | Convey("Can iterate tree items at `/test/zen/sub-o` with `path`", t, func() { 331 | i := 0 332 | c.Root().Path([]byte("/test/zen/sub-o"), func(k []byte, v *List) (e bool) { 333 | i++ 334 | return 335 | }) 336 | So(i, ShouldEqual, 2) 337 | }) 338 | 339 | Convey("Can iterate tree items at `/test/zen/sub-one` with `path`", t, func() { 340 | i := 0 341 | c.Root().Path([]byte("/test/zen/sub-one"), func(k []byte, v *List) (e bool) { 342 | i++ 343 | return 344 | }) 345 | So(i, ShouldEqual, 3) 346 | }) 347 | 348 | Convey("Can iterate tree items at `/test/zen/sub` with `path` and exit", t, func() { 349 | i := 0 350 | c.Root().Path([]byte("/test/zen/sub"), func(k []byte, v *List) (e bool) { 351 | i++ 352 | return true 353 | }) 354 | So(i, ShouldEqual, 1) 355 | }) 356 | 357 | } 358 | 359 | func TestIritate(t *testing.T) { 360 | 361 | c := New().Copy() 362 | 363 | i := c.Cursor() 364 | 365 | Convey("Can iterate to the min with no items", t, func() { 366 | k, v := i.First() 367 | So(v, ShouldBeNil) 368 | So(k, ShouldBeNil) 369 | }) 370 | 371 | Convey("Can iterate to the max with no items", t, func() { 372 | k, v := i.Last() 373 | So(v, ShouldBeNil) 374 | So(k, ShouldBeNil) 375 | }) 376 | 377 | Convey("Can seek to a key with no items", t, func() { 378 | k, v := i.Seek([]byte("")) 379 | So(v, ShouldBeNil) 380 | So(k, ShouldBeNil) 381 | }) 382 | 383 | Convey("Can seek to a key with no items", t, func() { 384 | k, v := i.Seek([]byte("/something")) 385 | So(v, ShouldBeNil) 386 | So(k, ShouldBeNil) 387 | }) 388 | 389 | } 390 | 391 | func TestIterate(t *testing.T) { 392 | 393 | c := New().Copy() 394 | 395 | Convey("Can insert tree items", t, func() { 396 | for _, v := range s { 397 | c.Put(0, []byte(v), []byte(v)) 398 | } 399 | So(c.Size(), ShouldEqual, 35) 400 | }) 401 | 402 | i := c.Cursor() 403 | 404 | Convey("Can get iterator", t, func() { 405 | So(i, ShouldNotBeNil) 406 | }) 407 | 408 | Convey("Prev with no seek returns nil", t, func() { 409 | k, v := i.Prev() 410 | So(k, ShouldBeNil) 411 | So(v, ShouldBeNil) 412 | }) 413 | 414 | Convey("Next with no seek returns nil", t, func() { 415 | k, v := i.Next() 416 | So(k, ShouldBeNil) 417 | So(v, ShouldBeNil) 418 | }) 419 | 420 | Convey("Can iterate to the min", t, func() { 421 | k, v := i.First() 422 | var t []int 423 | for _, q := range i.nums { 424 | t = append(t, q) 425 | } 426 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[0])) 427 | So(k, ShouldResemble, []byte(s[0])) 428 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[0])) 429 | }) 430 | 431 | Convey("Can iterate using `next`", t, func() { 432 | for j := 1; j < len(s); j++ { 433 | k, v := i.Next() 434 | var t []int 435 | for _, q := range i.nums { 436 | t = append(t, q) 437 | } 438 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[j])) 439 | So(k, ShouldResemble, []byte(s[j])) 440 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[j])) 441 | } 442 | }) 443 | 444 | Convey("Next item is nil and doesn't change cursor", t, func() { 445 | k, v := i.Next() 446 | So(k, ShouldBeNil) 447 | So(v, ShouldBeNil) 448 | }) 449 | 450 | Convey("Can iterate to the max", t, func() { 451 | k, v := i.Last() 452 | var t []int 453 | for _, q := range i.nums { 454 | t = append(t, q) 455 | } 456 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[len(p)-1])) 457 | So(k, ShouldResemble, []byte(s[len(p)-1])) 458 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[len(p)-1])) 459 | }) 460 | 461 | Convey("Can iterate using `prev`", t, func() { 462 | for j := len(s) - 2; j >= 0; j-- { 463 | k, v := i.Prev() 464 | var t []int 465 | for _, q := range i.nums { 466 | t = append(t, q) 467 | } 468 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[j])) 469 | So(k, ShouldResemble, []byte(s[j])) 470 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[j])) 471 | } 472 | }) 473 | 474 | Convey("Prev item is nil and doesn't change cursor", t, func() { 475 | k, v := i.Prev() 476 | So(k, ShouldBeNil) 477 | So(v, ShouldBeNil) 478 | }) 479 | 480 | Convey("Seek nonexistant nil", t, func() { 481 | k, v := i.Seek(nil) 482 | So(k, ShouldResemble, []byte(s[0])) 483 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[0])) 484 | }) 485 | 486 | Convey("Seek nonexistant first byte", t, func() { 487 | k, v := i.Seek([]byte{0}) 488 | So(k, ShouldResemble, []byte(s[0])) 489 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[0])) 490 | }) 491 | 492 | Convey("Seek nonexistant first item", t, func() { 493 | k, v := i.Seek([]byte("/aaa")) 494 | So(k, ShouldResemble, []byte(s[0])) 495 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[0])) 496 | }) 497 | 498 | Convey("Seek just over last item", t, func() { 499 | k, v := i.Seek([]byte("/zoo/some/path/-")) 500 | So(v, ShouldBeNil) 501 | So(k, ShouldBeNil) 502 | }) 503 | 504 | Convey("Seek nonexistant last item", t, func() { 505 | k, v := i.Seek([]byte("/zzz")) 506 | So(v, ShouldBeNil) 507 | So(k, ShouldBeNil) 508 | }) 509 | 510 | Convey("Seek nonexistant last byte", t, func() { 511 | k, v := i.Seek([]byte{255}) 512 | So(v, ShouldBeNil) 513 | So(k, ShouldBeNil) 514 | }) 515 | 516 | Convey("Seek half item is correct", t, func() { 517 | k, v := i.Seek([]byte(s[10][:len(s[10])-3])) 518 | var t []int 519 | for _, q := range i.nums { 520 | t = append(t, q) 521 | } 522 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[10])) 523 | So(k, ShouldResemble, []byte(s[10])) 524 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[10])) 525 | }) 526 | 527 | Convey("Seek full item is correct", t, func() { 528 | k, v := i.Seek([]byte(s[10])) 529 | var t []int 530 | for _, q := range i.nums { 531 | t = append(t, q) 532 | } 533 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[10])) 534 | So(k, ShouldResemble, []byte(s[10])) 535 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[10])) 536 | }) 537 | 538 | Convey("Seek overfull item is correct", t, func() { 539 | k, v := i.Seek([]byte(s[10] + "-")) 540 | var t []int 541 | for _, q := range i.nums { 542 | t = append(t, q) 543 | } 544 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[11])) 545 | So(k, ShouldResemble, []byte(s[11])) 546 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[11])) 547 | }) 548 | 549 | Convey("Seek finishing item is correct", t, func() { 550 | k, v := i.Seek([]byte("/test/zzz")) 551 | var t []int 552 | for _, q := range i.nums { 553 | t = append(t, q) 554 | } 555 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[32])) 556 | So(k, ShouldResemble, []byte(s[32])) 557 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[32])) 558 | }) 559 | 560 | Convey("Seek finalising item is correct", t, func() { 561 | k, v := i.Seek([]byte("/zoo/some/xxxx")) 562 | So(v, ShouldBeNil) 563 | So(k, ShouldBeNil) 564 | }) 565 | 566 | Convey("Prev item after seek is correct", t, func() { 567 | i.Seek([]byte(s[10])) 568 | k, v := i.Prev() 569 | var t []int 570 | for _, q := range i.nums { 571 | t = append(t, q) 572 | } 573 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[9])) 574 | So(k, ShouldResemble, []byte(s[9])) 575 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[9])) 576 | }) 577 | 578 | Convey("Next item after seek is correct", t, func() { 579 | i.Seek([]byte(s[10])) 580 | k, v := i.Next() 581 | var t []int 582 | for _, q := range i.nums { 583 | t = append(t, q) 584 | } 585 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[11])) 586 | So(k, ShouldResemble, []byte(s[11])) 587 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[11])) 588 | }) 589 | 590 | Convey("FINAL", t, func() { 591 | i.Seek([]byte(s[10])) 592 | i.Del() 593 | k, v := i.Next() 594 | var t []int 595 | for _, q := range i.nums { 596 | t = append(t, q) 597 | } 598 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[11])) 599 | So(k, ShouldResemble, []byte(s[11])) 600 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[11])) 601 | }) 602 | 603 | Convey("FINAL", t, func() { 604 | var k []byte 605 | i.Seek([]byte(s[10])) 606 | i.Del() 607 | i.Next() 608 | i.Del() 609 | i.Next() 610 | i.Del() 611 | i.Next() 612 | i.Del() 613 | i.Next() 614 | i.Del() 615 | k, v := i.Next() 616 | var t []int 617 | for _, q := range i.nums { 618 | t = append(t, q) 619 | } 620 | So(fmt.Sprint(t), ShouldEqual, fmt.Sprint(p[15])) 621 | So(k, ShouldResemble, []byte(s[15])) 622 | So(v.Get(0, Upto).Val(), ShouldResemble, []byte(s[15])) 623 | }) 624 | 625 | } 626 | 627 | func TestUpdate(t *testing.T) { 628 | 629 | c := New().Copy() 630 | 631 | Convey("Can insert 1st item", t, func() { 632 | val := c.Put(0, []byte("/test"), []byte("ONE")) 633 | So(val, ShouldBeNil) 634 | So(val, ShouldEqual, nil) 635 | So(c.Size(), ShouldEqual, 1) 636 | So(c.Get(0, []byte("/test")).Val(), ShouldResemble, []byte("ONE")) 637 | }) 638 | 639 | Convey("Can insert 2nd item", t, func() { 640 | val := c.Put(0, []byte("/test"), []byte("TWO")) 641 | So(val, ShouldNotBeNil) 642 | // So(val.Val(), ShouldResemble, []byte("ONE")) 643 | So(c.Size(), ShouldEqual, 1) 644 | So(c.Get(0, []byte("/test")).Val(), ShouldResemble, []byte("TWO")) 645 | }) 646 | 647 | Convey("Can insert 3rd item", t, func() { 648 | val := c.Put(0, []byte("/test"), []byte("TRE")) 649 | So(val, ShouldNotBeNil) 650 | // So(val.Val(), ShouldResemble, []byte("TWO")) 651 | So(c.Size(), ShouldEqual, 1) 652 | So(c.Get(0, []byte("/test")).Val(), ShouldResemble, []byte("TRE")) 653 | }) 654 | 655 | } 656 | 657 | func TestDelete(t *testing.T) { 658 | 659 | c := New().Copy() 660 | 661 | Convey("Can insert 1st item", t, func() { 662 | val := c.Put(0, []byte("/test"), []byte("TEST")) 663 | So(val, ShouldBeNil) 664 | So(val, ShouldEqual, nil) 665 | So(c.Size(), ShouldEqual, 1) 666 | So(c.Get(0, []byte("/test")).Val(), ShouldResemble, []byte("TEST")) 667 | }) 668 | 669 | Convey("Can delete 1st item", t, func() { 670 | val := c.Cut([]byte("/test")) 671 | So(val, ShouldNotBeNil) 672 | So(val.Val(), ShouldResemble, []byte("TEST")) 673 | So(c.Size(), ShouldEqual, 0) 674 | So(c.Get(0, []byte("/test")), ShouldBeNil) 675 | }) 676 | 677 | Convey("Can delete 1st item", t, func() { 678 | val := c.Cut([]byte("/test")) 679 | So(val, ShouldBeNil) 680 | So(val, ShouldEqual, nil) 681 | So(c.Size(), ShouldEqual, 0) 682 | So(c.Get(0, []byte("/test")), ShouldBeNil) 683 | }) 684 | 685 | } 686 | 687 | func TestVersion(t *testing.T) { 688 | 689 | c := New().Copy() 690 | 691 | Convey("Can insert a 1st versioned item", t, func() { 692 | val := c.Put(5, []byte("/one"), []byte("ONE")) 693 | So(val, ShouldBeNil) 694 | So(c.Size(), ShouldEqual, 1) 695 | So(c.Get(3, []byte("/one")), ShouldBeNil) 696 | So(c.Get(5, []byte("/one")).Val(), ShouldResemble, []byte("ONE")) 697 | So(c.Get(7, []byte("/one")).Val(), ShouldResemble, []byte("ONE")) 698 | }) 699 | 700 | Convey("Can update a 1st versioned item", t, func() { 701 | val := c.Put(6, []byte("/one"), []byte("ONE-NEW")) 702 | So(val, ShouldNotBeNil) 703 | So(val.Val(), ShouldResemble, []byte("ONE")) 704 | So(c.Size(), ShouldEqual, 1) 705 | So(c.Get(3, []byte("/one")), ShouldBeNil) 706 | So(c.Get(5, []byte("/one")).Val(), ShouldResemble, []byte("ONE")) 707 | So(c.Get(7, []byte("/one")).Val(), ShouldResemble, []byte("ONE-NEW")) 708 | }) 709 | 710 | Convey("Can iterate to the item", t, func() { 711 | i := c.Cursor() 712 | k, v := i.Seek([]byte("/one")) 713 | So(k, ShouldResemble, []byte("/one")) 714 | So(v.Get(m, Upto).Val(), ShouldResemble, []byte("ONE-NEW")) 715 | So(i.Here().Get(0, Upto), ShouldBeNil) 716 | So(i.Here().Get(1, Upto), ShouldBeNil) 717 | So(i.Here().Min().Val(), ShouldResemble, []byte("ONE")) 718 | So(i.Here().Max().Val(), ShouldResemble, []byte("ONE-NEW")) 719 | So(i.Here().Get(5, Upto).Ver(), ShouldEqual, 5) 720 | So(i.Here().Get(5, Upto).Val(), ShouldResemble, []byte("ONE")) 721 | So(i.Here().Get(6, Upto).Ver(), ShouldEqual, 6) 722 | So(i.Here().Get(6, Upto).Val(), ShouldResemble, []byte("ONE-NEW")) 723 | So(i.Here().Get(9, Upto).Ver(), ShouldEqual, 6) 724 | So(i.Here().Get(9, Upto).Val(), ShouldResemble, []byte("ONE-NEW")) 725 | So(i.Here().Get(m, Upto).Ver(), ShouldEqual, 6) 726 | So(i.Here().Get(m, Upto).Val(), ShouldResemble, []byte("ONE-NEW")) 727 | }) 728 | 729 | Convey("Can iterate and walk over the item and exit", t, func() { 730 | var ts []uint64 731 | i := c.Cursor() 732 | i.Seek([]byte("/one")) 733 | i.Here().Walk(func(x *Item) (exit bool) { 734 | ts = append(ts, x.Ver()) 735 | return true 736 | }) 737 | So(ts, ShouldHaveLength, 1) 738 | }) 739 | 740 | Convey("Can iterate and walk over the item and continue", t, func() { 741 | var ts []uint64 742 | i := c.Cursor() 743 | i.Seek([]byte("/one")) 744 | i.Here().Walk(func(x *Item) (exit bool) { 745 | ts = append(ts, x.Ver()) 746 | return false 747 | }) 748 | So(ts, ShouldHaveLength, 2) 749 | }) 750 | 751 | Convey("Can remove a 1st versioned item", t, func() { 752 | val := c.Put(8, []byte("/one"), nil) 753 | So(val.Val(), ShouldNotBeNil) 754 | So(val.Val(), ShouldResemble, []byte("ONE-NEW")) 755 | So(c.Size(), ShouldEqual, 1) 756 | So(c.Get(0, []byte("/one")), ShouldBeNil) 757 | So(c.Get(3, []byte("/one")), ShouldBeNil) 758 | So(c.Get(5, []byte("/one")).Val(), ShouldResemble, []byte("ONE")) 759 | So(c.Get(7, []byte("/one")).Val(), ShouldResemble, []byte("ONE-NEW")) 760 | So(c.Get(9, []byte("/one")).Val(), ShouldBeNil) 761 | }) 762 | 763 | Convey("Can iterate to the item once removed", t, func() { 764 | i := c.Cursor() 765 | k, v := i.Seek([]byte("/one")) 766 | So(k, ShouldResemble, []byte("/one")) 767 | So(v.Get(0, Upto), ShouldBeNil) 768 | So(i.Here().Get(0, Upto), ShouldBeNil) 769 | So(i.Here().Get(3, Upto), ShouldBeNil) 770 | So(i.Here().Get(5, Upto).Val(), ShouldResemble, []byte("ONE")) 771 | So(i.Here().Get(7, Upto).Val(), ShouldResemble, []byte("ONE-NEW")) 772 | So(i.Here().Get(9, Upto).Val(), ShouldResemble, []byte(nil)) 773 | }) 774 | 775 | Convey("Can insert a 2nd versioned item", t, func() { 776 | val := c.Put(5, []byte("/two"), []byte("TWO")) 777 | So(val, ShouldBeNil) 778 | So(c.Size(), ShouldEqual, 2) 779 | So(c.Get(3, []byte("/two")), ShouldBeNil) 780 | So(c.Get(5, []byte("/two")).Val(), ShouldResemble, []byte("TWO")) 781 | So(c.Get(7, []byte("/two")).Val(), ShouldResemble, []byte("TWO")) 782 | }) 783 | 784 | Convey("Can update a 2nd versioned item", t, func() { 785 | val := c.Put(6, []byte("/two"), []byte("TWO-NEW")) 786 | So(val.Val(), ShouldNotBeNil) 787 | So(val.Val(), ShouldResemble, []byte("TWO")) 788 | So(c.Size(), ShouldEqual, 2) 789 | So(c.Get(3, []byte("/two")), ShouldBeNil) 790 | So(c.Get(5, []byte("/two")).Val(), ShouldResemble, []byte("TWO")) 791 | So(c.Get(7, []byte("/two")).Val(), ShouldResemble, []byte("TWO-NEW")) 792 | }) 793 | 794 | Convey("Can iterate to the item", t, func() { 795 | i := c.Cursor() 796 | k, v := i.Seek([]byte("/two")) 797 | So(k, ShouldResemble, []byte("/two")) 798 | So(v.Get(m, Upto).Val(), ShouldResemble, []byte("TWO-NEW")) 799 | So(i.Here().Get(0, Upto), ShouldBeNil) 800 | So(i.Here().Get(1, Upto), ShouldBeNil) 801 | So(i.Here().Min().Val(), ShouldResemble, []byte("TWO")) 802 | So(i.Here().Max().Val(), ShouldResemble, []byte("TWO-NEW")) 803 | So(i.Here().Get(5, Upto).Ver(), ShouldEqual, 5) 804 | So(i.Here().Get(5, Upto).Val(), ShouldResemble, []byte("TWO")) 805 | So(i.Here().Get(6, Upto).Ver(), ShouldEqual, 6) 806 | So(i.Here().Get(6, Upto).Val(), ShouldResemble, []byte("TWO-NEW")) 807 | So(i.Here().Get(9, Upto).Ver(), ShouldEqual, 6) 808 | So(i.Here().Get(9, Upto).Val(), ShouldResemble, []byte("TWO-NEW")) 809 | So(i.Here().Get(m, Upto).Ver(), ShouldEqual, 6) 810 | So(i.Here().Get(m, Upto).Val(), ShouldResemble, []byte("TWO-NEW")) 811 | }) 812 | 813 | Convey("Can iterate and walk over the item and exit", t, func() { 814 | var ts []uint64 815 | i := c.Cursor() 816 | i.Seek([]byte("/two")) 817 | i.Here().Walk(func(x *Item) (exit bool) { 818 | ts = append(ts, x.Ver()) 819 | return true 820 | }) 821 | So(ts, ShouldHaveLength, 1) 822 | }) 823 | 824 | Convey("Can iterate and walk over the item and continue", t, func() { 825 | var ts []uint64 826 | i := c.Cursor() 827 | i.Seek([]byte("/two")) 828 | i.Here().Walk(func(x *Item) (exit bool) { 829 | ts = append(ts, x.Ver()) 830 | return false 831 | }) 832 | So(ts, ShouldHaveLength, 2) 833 | }) 834 | 835 | Convey("Can remove a 2nd versioned item", t, func() { 836 | val := c.Put(8, []byte("/two"), nil) 837 | So(val.Val(), ShouldNotBeNil) 838 | So(val.Val(), ShouldResemble, []byte("TWO-NEW")) 839 | So(c.Size(), ShouldEqual, 2) 840 | So(c.Get(0, []byte("/two")), ShouldBeNil) 841 | So(c.Get(3, []byte("/two")), ShouldBeNil) 842 | So(c.Get(5, []byte("/two")).Val(), ShouldResemble, []byte("TWO")) 843 | So(c.Get(7, []byte("/two")).Val(), ShouldResemble, []byte("TWO-NEW")) 844 | So(c.Get(9, []byte("/two")).Val(), ShouldBeNil) 845 | }) 846 | 847 | Convey("Can iterate to the item once removed", t, func() { 848 | i := c.Cursor() 849 | k, v := i.Seek([]byte("/two")) 850 | So(k, ShouldResemble, []byte("/two")) 851 | So(v.Get(0, Upto), ShouldBeNil) 852 | So(i.Here().Get(0, Upto), ShouldBeNil) 853 | So(i.Here().Get(3, Upto), ShouldBeNil) 854 | So(i.Here().Get(5, Upto).Val(), ShouldResemble, []byte("TWO")) 855 | So(i.Here().Get(7, Upto).Val(), ShouldResemble, []byte("TWO-NEW")) 856 | So(i.Here().Get(9, Upto).Val(), ShouldResemble, []byte(nil)) 857 | }) 858 | 859 | Convey("Can delete the latest version", t, func() { 860 | val := c.Del(5, []byte("/one")) 861 | So(val.Val(), ShouldNotBeNil) 862 | So(val.Val(), ShouldResemble, []byte("ONE")) 863 | So(c.Size(), ShouldEqual, 2) 864 | So(c.Get(3, []byte("/one")), ShouldBeNil) 865 | So(c.Get(5, []byte("/one")).Val(), ShouldBeNil) 866 | So(c.Get(7, []byte("/one")).Val(), ShouldResemble, []byte("ONE-NEW")) 867 | }) 868 | 869 | Convey("Can delete invalid version", t, func() { 870 | val := c.Del(3, []byte("/one")) 871 | So(val.Val(), ShouldBeNil) 872 | So(c.Size(), ShouldEqual, 2) 873 | So(c.Get(3, []byte("/one")).Val(), ShouldBeNil) 874 | So(c.Get(5, []byte("/one")).Val(), ShouldBeNil) 875 | So(c.Get(7, []byte("/one")).Val(), ShouldResemble, []byte("ONE-NEW")) 876 | }) 877 | 878 | Convey("Can delete whole key", t, func() { 879 | val := c.Cut([]byte("/one")) 880 | So(val.Val(), ShouldBeNil) 881 | So(c.Size(), ShouldEqual, 1) 882 | So(c.Get(3, []byte("/one")), ShouldBeNil) 883 | So(c.Get(5, []byte("/one")), ShouldBeNil) 884 | So(c.Get(7, []byte("/one")), ShouldBeNil) 885 | }) 886 | 887 | Convey("Can delete whole key", t, func() { 888 | val := c.Cut([]byte("/two")) 889 | So(val.Val(), ShouldBeNil) 890 | So(c.Size(), ShouldEqual, 0) 891 | So(c.Get(3, []byte("/two")), ShouldBeNil) 892 | So(c.Get(5, []byte("/two")), ShouldBeNil) 893 | So(c.Get(7, []byte("/two")), ShouldBeNil) 894 | }) 895 | 896 | } 897 | -------------------------------------------------------------------------------- /db.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | import ( 4 | "bytes" 5 | "io" 6 | "os" 7 | "strings" 8 | "sync" 9 | "sync/atomic" 10 | "time" 11 | "unsafe" 12 | 13 | "github.com/surrealdb/rixxdb/data" 14 | ) 15 | 16 | // DB represents a database which operates in memory, and persists to 17 | // disk. A DB is safe to use from multiple goroutines concurrently. A 18 | // DB can have only one read-write transaction open at a time, but 19 | // allows an unlimited number of concurrent read-only transactions at 20 | // a time, each with its own consistent view of the data as it existed 21 | // when the transaction started. 22 | type DB struct { 23 | done bool 24 | kind string 25 | path string 26 | conf *Config 27 | lock sync.Mutex 28 | tree unsafe.Pointer 29 | wait struct { 30 | flush, shrink bool 31 | } 32 | tick struct { 33 | flush, shrink *time.Ticker 34 | } 35 | temp struct { 36 | lock sync.Mutex 37 | pntr *bytes.Buffer 38 | } 39 | buff struct { 40 | lock sync.Mutex 41 | pntr *bytes.Buffer 42 | } 43 | file struct { 44 | lock sync.Mutex 45 | pntr *os.File 46 | } 47 | shrk struct { 48 | lock sync.Mutex 49 | pntr *os.File 50 | } 51 | } 52 | 53 | // Open creates and opens a database at the given path. If the file 54 | // does not exist then it will be created automatically. Passing 55 | // in a nil Config will cause Rixx to use the default options. 56 | func Open(path string, conf *Config) (*DB, error) { 57 | 58 | db := &DB{ 59 | done: false, 60 | path: path, 61 | conf: conf, 62 | kind: "memory", 63 | tree: unsafe.Pointer(data.New()), 64 | } 65 | 66 | // Check that if there is an encryption key specified 67 | // on DB creation, that the key is of the correct length 68 | // for AES-128, AES-192, or AES-256 encryption. 69 | 70 | if conf.EncryptionKey != nil { 71 | if l := len(conf.EncryptionKey); l != 16 && l != 24 && l != 32 { 72 | return nil, ErrDbInvalidEncryptionKey 73 | } 74 | } 75 | 76 | // If the database has been specified to sync to disk 77 | // then setup the syncr process, and read any data off 78 | // the stream before enabling writing to storage. 79 | 80 | if path != "memory" { 81 | 82 | var err error 83 | 84 | db.kind = "file" 85 | 86 | db.path = strings.TrimPrefix(db.path, "file://") 87 | 88 | // Create a buffer for txn writes 89 | db.temp.pntr = bytes.NewBuffer(nil) 90 | 91 | // Create a buffer for async writes 92 | db.buff.pntr = bytes.NewBuffer(nil) 93 | 94 | // Open the file at the specified path. 95 | if db.file.pntr, err = db.open(db.path); err != nil { 96 | return nil, err 97 | } 98 | 99 | // Go back to beg of file for reading. 100 | if _, err = db.file.pntr.Seek(0, 0); err != nil { 101 | db.file.pntr.Close() 102 | return nil, err 103 | } 104 | 105 | if err = db.Load(db.file.pntr); err != nil { 106 | db.file.pntr.Close() 107 | return nil, err 108 | } 109 | 110 | // Go back to end of file for writing. 111 | if _, err = db.file.pntr.Seek(0, 2); err != nil { 112 | db.file.pntr.Close() 113 | return nil, err 114 | } 115 | 116 | } 117 | 118 | go db.flush() 119 | go db.shrnk() 120 | 121 | return db, nil 122 | 123 | } 124 | 125 | func (db *DB) flush() { 126 | 127 | if db.file.pntr == nil { 128 | return 129 | } 130 | 131 | if db.conf.FlushPolicy < 0 { 132 | return 133 | } 134 | 135 | if db.conf.FlushPolicy > 0 { 136 | 137 | db.tick.flush = time.NewTicker(db.conf.FlushPolicy) 138 | 139 | defer db.tick.flush.Stop() 140 | 141 | for range db.tick.flush.C { 142 | if err := db.Flush(); err != nil { 143 | panic(err) 144 | } 145 | } 146 | 147 | } 148 | 149 | } 150 | 151 | func (db *DB) shrnk() { 152 | 153 | if db.file.pntr == nil { 154 | return 155 | } 156 | 157 | if db.conf.ShrinkPolicy < 0 { 158 | return 159 | } 160 | 161 | if db.conf.ShrinkPolicy > 0 { 162 | 163 | db.tick.shrink = time.NewTicker(db.conf.ShrinkPolicy) 164 | 165 | defer db.tick.shrink.Stop() 166 | 167 | for range db.tick.shrink.C { 168 | if err := db.Shrink(); err != nil { 169 | panic(err) 170 | } 171 | } 172 | 173 | } 174 | 175 | } 176 | 177 | func (db *DB) open(path string) (*os.File, error) { 178 | 179 | return os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_RDWR, 0666) 180 | 181 | } 182 | 183 | func (db *DB) root() *data.Tree { 184 | 185 | return (*data.Tree)(atomic.LoadPointer(&db.tree)) 186 | 187 | } 188 | 189 | func (db *DB) push(ops []*op) error { 190 | 191 | // If there is no file associated 192 | // with this database then ignore 193 | // this method call. 194 | 195 | if db.file.pntr == nil { 196 | return nil 197 | } 198 | 199 | // If the database FlushPolicy has 200 | // been disabled, then ignore this 201 | // call to sync the data buffer. 202 | 203 | if db.conf.FlushPolicy < 0 { 204 | return nil 205 | } 206 | 207 | // Reset the transaction buffer so 208 | // that the next transaction can 209 | // use it without reallocating. 210 | 211 | defer db.temp.pntr.Reset() 212 | 213 | // Build the transaction alteration 214 | // buffer to memory so we can write 215 | // it to the file in one operation. 216 | 217 | for _, op := range ops { 218 | switch op.op { 219 | case clr: 220 | db.temp.pntr.WriteRune('C') 221 | db.temp.pntr.Write(wlen(op.key)) 222 | db.temp.pntr.Write(op.key) 223 | db.temp.pntr.WriteRune('\n') 224 | case del: 225 | db.temp.pntr.WriteRune('D') 226 | db.temp.pntr.Write(wver(op.ver)) 227 | db.temp.pntr.Write(wlen(op.key)) 228 | db.temp.pntr.Write(op.key) 229 | db.temp.pntr.WriteRune('\n') 230 | case put: 231 | db.temp.pntr.WriteRune('P') 232 | db.temp.pntr.Write(wver(op.ver)) 233 | db.temp.pntr.Write(wlen(op.key)) 234 | db.temp.pntr.Write(op.key) 235 | db.temp.pntr.Write(wlen(op.val)) 236 | db.temp.pntr.Write(op.val) 237 | db.temp.pntr.WriteRune('\n') 238 | } 239 | } 240 | 241 | // If the FlushPolicy is specified 242 | // asynchronous, write the data 243 | // to the buffer to sync later. 244 | 245 | if db.conf.FlushPolicy != 0 { 246 | 247 | db.buff.lock.Lock() 248 | defer db.buff.lock.Unlock() 249 | 250 | if _, err := db.buff.pntr.Write(db.temp.pntr.Bytes()); err != nil { 251 | return err 252 | } 253 | 254 | } 255 | 256 | // If the FlushPolicy is specified 257 | // to sync on every commit, then 258 | // ensure the data is synced now. 259 | 260 | if db.conf.FlushPolicy == 0 { 261 | 262 | db.file.lock.Lock() 263 | defer db.file.lock.Unlock() 264 | 265 | if _, err := db.file.pntr.Write(db.temp.pntr.Bytes()); err != nil { 266 | return err 267 | } 268 | 269 | if db.conf.SyncWrites == true { 270 | if err := db.file.pntr.Sync(); err != nil { 271 | return err 272 | } 273 | } 274 | 275 | } 276 | 277 | return nil 278 | 279 | } 280 | 281 | // Load loads database operations from a reader. This can be used to 282 | // playback a database snapshot into an already running database. 283 | func (db *DB) Load(r io.Reader) error { 284 | 285 | tx, err := db.Begin(true) 286 | if err != nil { 287 | return err 288 | } 289 | 290 | defer tx.Cancel() 291 | 292 | return tx.inj(r) 293 | 294 | } 295 | 296 | // Save saves all database operations to a writer. This can be used to 297 | // save a database snapshot to a secondary file or stream. 298 | func (db *DB) Save(w io.Writer) error { 299 | 300 | var tx *TX 301 | var err error 302 | 303 | tx, err = db.Begin(false) 304 | if err != nil { 305 | return err 306 | } 307 | 308 | defer tx.Cancel() 309 | 310 | cur := tx.tree.Cursor() 311 | 312 | for k, l := cur.First(); k != nil; k, l = cur.Next() { 313 | 314 | l.Walk(func(i *data.Item) (e bool) { 315 | _, err = w.Write(tx.out(i.Ver(), k, i.Val())) 316 | return err != nil 317 | }) 318 | 319 | if err != nil { 320 | return err 321 | } 322 | 323 | } 324 | 325 | return nil 326 | 327 | } 328 | 329 | // Flush ensures that all database operations are flushed to the 330 | // underlying storage. If the database is currently performing 331 | // a shrink from a previous call to this method, then the call 332 | // will be ignored. This does nothing on in-memory databases. 333 | func (db *DB) Flush() error { 334 | 335 | // If there is no file associated 336 | // with this database then ignore 337 | // this method call. 338 | 339 | if db.file.pntr == nil { 340 | return ErrDbMemoryOnly 341 | } 342 | 343 | // If the database is currently 344 | // already syncing, then ignore 345 | // the flush this time around. 346 | 347 | if db.wait.flush { 348 | return ErrDbAlreadySyncing 349 | } 350 | 351 | // Mark that the database is now 352 | // syncing so that other calls 353 | // to sync will be ignored. 354 | 355 | db.wait.flush = true 356 | 357 | // Ensure that when this method 358 | // is finished we mark that the 359 | // database is not syncing. 360 | 361 | defer func() { 362 | db.wait.flush = false 363 | }() 364 | 365 | // Obtain a lock on the buffer to 366 | // prevent changes while we flush 367 | // the buffer to the sender. 368 | 369 | db.buff.lock.Lock() 370 | defer db.buff.lock.Unlock() 371 | 372 | // Obtain a lock on the file to 373 | // prevent other threads from 374 | // syncing to the file. 375 | 376 | db.file.lock.Lock() 377 | defer db.file.lock.Unlock() 378 | 379 | // Flush the buffer to the file 380 | // and ensure that the file is 381 | // synced to storage in the OS. 382 | 383 | if _, err := db.buff.pntr.WriteTo(db.file.pntr); err != nil { 384 | return err 385 | } 386 | 387 | if err := db.file.pntr.Sync(); err != nil { 388 | return err 389 | } 390 | 391 | return nil 392 | 393 | } 394 | 395 | // Shrink ensures that all unnecessary database operations that 396 | // have been flushed to disk are removed, reducing the output 397 | // of the append-only log files. If the database is currently 398 | // performing a shrink from a previous call to this method, 399 | // then the call will be ignored. This only works for certain 400 | // storage types, and does nothing on in-memory databases. 401 | func (db *DB) Shrink() error { 402 | 403 | // If there is no file associated 404 | // with this database then ignore 405 | // this method call. 406 | 407 | if db.file.pntr == nil { 408 | return ErrDbMemoryOnly 409 | } 410 | 411 | // If the database is currently 412 | // already shrinking, then ignore 413 | // the shrink this time around. 414 | 415 | if db.wait.shrink { 416 | return ErrDbAlreadyShrinking 417 | } 418 | 419 | // Mark that the database is now 420 | // shrinking so that other calls 421 | // to sync will be ignored. 422 | 423 | db.wait.shrink = true 424 | 425 | // Ensure that when this method 426 | // is finished we mark that the 427 | // database is not shrinking. 428 | 429 | defer func() { 430 | db.wait.shrink = false 431 | }() 432 | 433 | // Obtain a lock on the file to 434 | // prevent other threads from 435 | // syncing to the file. 436 | 437 | db.file.lock.Lock() 438 | defer db.file.lock.Unlock() 439 | 440 | // If the current data storage 441 | // type is file, then write the 442 | // data, and rotate the files. 443 | 444 | if db.kind == "file" { 445 | 446 | var err error 447 | 448 | // Write all of the current 449 | // PUT data to a temporary 450 | // file which we will rotate. 451 | 452 | if db.shrk.pntr, err = db.open(db.path + ".tmp"); err != nil { 453 | return err 454 | } 455 | 456 | // Save a current snapshot of 457 | // all of the database content 458 | // to the temporary file. 459 | 460 | if err = db.Save(db.shrk.pntr); err != nil { 461 | return err 462 | } 463 | 464 | // Close the temporary file 465 | // pointer as we have written 466 | // the snapshot data to it. 467 | 468 | if err = db.shrk.pntr.Close(); err != nil { 469 | return err 470 | } 471 | 472 | // Close the current pointer 473 | // to the data file so that we 474 | // can rotate the files. 475 | 476 | if err = db.file.pntr.Close(); err != nil { 477 | return err 478 | } 479 | 480 | // Rotate the temporary file 481 | // into the main data file by 482 | // renaming the temporary file. 483 | 484 | if err = os.Rename(db.path+".tmp", db.path); err != nil { 485 | return err 486 | } 487 | 488 | // Rotate the temporary file 489 | // into the main data file and 490 | // obtain a new file reference. 491 | 492 | if db.file.pntr, err = db.open(db.path); err != nil { 493 | return err 494 | } 495 | 496 | } 497 | 498 | return nil 499 | 500 | } 501 | 502 | // Close waits for all transactions to finish and releeases resources. 503 | func (db *DB) Close() error { 504 | 505 | var err error 506 | 507 | if db.done { 508 | return ErrDbClosed 509 | } 510 | 511 | db.lock.Lock() 512 | defer db.lock.Unlock() 513 | 514 | db.buff.lock.Lock() 515 | defer db.buff.lock.Unlock() 516 | 517 | db.file.lock.Lock() 518 | defer db.file.lock.Unlock() 519 | 520 | if db.tick.flush != nil { 521 | db.tick.flush.Stop() 522 | db.tick.flush = nil 523 | } 524 | 525 | if db.tick.shrink != nil { 526 | db.tick.shrink.Stop() 527 | db.tick.shrink = nil 528 | } 529 | 530 | defer func() { db.tree, db.path, db.done = nil, "", true }() 531 | 532 | if db.temp.pntr != nil { 533 | defer func() { db.temp.pntr = nil }() 534 | db.temp.pntr.Reset() 535 | } 536 | 537 | if db.buff.pntr != nil { 538 | defer func() { db.buff.pntr = nil }() 539 | if _, err = db.buff.pntr.WriteTo(db.file.pntr); err != nil { 540 | return err 541 | } 542 | } 543 | 544 | if db.file.pntr != nil { 545 | if db.conf.SyncWrites == true { 546 | if err = db.file.pntr.Sync(); err != nil { 547 | return err 548 | } 549 | } 550 | } 551 | 552 | if db.file.pntr != nil { 553 | defer func() { db.file.pntr = nil }() 554 | if err = db.file.pntr.Close(); err != nil { 555 | return err 556 | } 557 | } 558 | 559 | return nil 560 | 561 | } 562 | 563 | // Begin starts a new transaction. Multiple read-only transactions can 564 | // be used concurrently but only one write transaction can be used at 565 | // a time. Starting multiple write transactions will cause the calls 566 | // to be serialized until the current write transaction finishes. 567 | func (db *DB) Begin(writeable bool) (*TX, error) { 568 | 569 | if db.done { 570 | return nil, ErrDbClosed 571 | } 572 | 573 | if writeable { 574 | db.lock.Lock() 575 | } 576 | 577 | tx := &TX{ 578 | db: db, 579 | rw: writeable, 580 | tree: db.root().Copy(), 581 | } 582 | 583 | return tx, nil 584 | 585 | } 586 | 587 | // View executes a function within the context of a managed read-only 588 | // transaction. Any error that is returned from the function is 589 | // returned from the View() method. Attempting to manually rollback 590 | // within the function will cause a panic. 591 | func (db *DB) View(fn func(*TX) error) error { 592 | 593 | tx, err := db.Begin(false) 594 | if err != nil { 595 | return err 596 | } 597 | 598 | // If the executed function panics 599 | // then ensure that we rollback and 600 | // clear up this transaction. 601 | 602 | defer func() { 603 | if tx.db != nil { 604 | tx.cancel() 605 | } 606 | }() 607 | 608 | // Mark the transaction as managed 609 | // so that any outside code can not 610 | // manually call Cancel or Commit. 611 | 612 | tx.fn = true 613 | 614 | // Run the defined transaction in the 615 | // scope of the transactions, and 616 | // catch any errors received. 617 | 618 | err = fn(tx) 619 | 620 | // Mark the transaction as unmanaged 621 | // so that we can call the Cancel 622 | // or Commit methods to finish up. 623 | 624 | tx.fn = false 625 | 626 | // If an error is returned from the 627 | // executed function, then clear the 628 | // transaction and return the error. 629 | 630 | if err != nil { 631 | tx.Cancel() 632 | return err 633 | } 634 | 635 | return tx.Cancel() 636 | 637 | } 638 | 639 | // Update executes a function within the context of a read-write 640 | // managed transaction. If no error is returned from the function 641 | // then the transaction is committed. If an error is returned then 642 | // the entire transaction is rolled back. Any error that is returned 643 | // from the function or returned from the commit is returned from 644 | // the Update() method. Attempting to manually commit or rollback 645 | // within the function will cause a panic. 646 | func (db *DB) Update(fn func(*TX) error) error { 647 | 648 | tx, err := db.Begin(true) 649 | if err != nil { 650 | return err 651 | } 652 | 653 | // If the executed function panics 654 | // then ensure that we rollback and 655 | // clear up this transaction. 656 | 657 | defer func() { 658 | if tx.db != nil { 659 | tx.cancel() 660 | } 661 | }() 662 | 663 | // Mark the transaction as managed 664 | // so that any outside code can not 665 | // manually call Cancel or Commit. 666 | 667 | tx.fn = true 668 | 669 | // Run the defined transaction in the 670 | // scope of the transactions, and 671 | // catch any errors received. 672 | 673 | err = fn(tx) 674 | 675 | // Mark the transaction as unmanaged 676 | // so that we can call the Cancel 677 | // or Commit methods to finish up. 678 | 679 | tx.fn = false 680 | 681 | // If an error is returned from the 682 | // executed function, then clear the 683 | // transaction and return the error. 684 | 685 | if err != nil { 686 | tx.Cancel() 687 | return err 688 | } 689 | 690 | return tx.Commit() 691 | 692 | } 693 | -------------------------------------------------------------------------------- /err.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | import "errors" 4 | 5 | // These errors can occur when opening or calling methods on a DB. 6 | var ( 7 | // ErrDbClosed occurs when a DB is accessed after it is closed. 8 | ErrDbClosed = errors.New("DB is not open") 9 | 10 | // ErrDbMemoryOnly occurs when persisting an in-memory DB. 11 | ErrDbMemoryOnly = errors.New("DB is memory-only") 12 | 13 | // ErrDbAlreadySyncing occurs when a sync is already in progress. 14 | ErrDbAlreadySyncing = errors.New("DB is already syncing") 15 | 16 | // ErrDbAlreadyShrinking occurs when a shrink is already in progress. 17 | ErrDbAlreadyShrinking = errors.New("DB is already shrinking") 18 | 19 | // ErrDbFileContentsInvalid occurs when the file is invalid or currupted. 20 | ErrDbFileContentsInvalid = errors.New("DB file contents invalid") 21 | 22 | // ErrDbInvalidEncryptionKey occurs when the provided encryption key is invalid. 23 | ErrDbInvalidEncryptionKey = errors.New("DB encryption key invalid") 24 | ) 25 | 26 | // These errors can occur when beginning or calling methods on a TX. 27 | var ( 28 | // ErrTxClosed occurs when cancelling or committing a closed transaction. 29 | ErrTxClosed = errors.New("TX is closed") 30 | 31 | // ErrTxNotWritable occurs when writing or committing a read-only transaction. 32 | ErrTxNotWritable = errors.New("TX is not writable") 33 | 34 | // ErrTxNotEditable occurs when calling manually closing a managed transaction. 35 | ErrTxNotEditable = errors.New("TX is not editable") 36 | 37 | // ErrKvNotExpectedValue occurs when using a nil key in put, select, or delete methods. 38 | ErrTxKeyCanNotBeNil = errors.New("TX key can not be nil") 39 | 40 | // ErrKvNotExpectedValue occurs when conditionally putting or deleting a key-value item. 41 | ErrTxNotExpectedValue = errors.New("KV val is not expected value") 42 | ) 43 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/surrealdb/rixxdb 2 | 3 | go 1.15 4 | 5 | require ( 6 | github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 // indirect 7 | github.com/smartystreets/assertions v1.2.0 // indirect 8 | github.com/smartystreets/goconvey v1.6.4 9 | ) 10 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= 2 | github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 3 | github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00 h1:l5lAOZEym3oK3SQ2HBHWsJUfbNBiTXJDeW2QDxw9AQ0= 4 | github.com/gopherjs/gopherjs v0.0.0-20200217142428-fce0ec30dd00/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= 5 | github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= 6 | github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= 7 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= 8 | github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= 9 | github.com/smartystreets/assertions v1.2.0 h1:42S6lae5dvLc7BrLu/0ugRtcFVjoJNMC/N3yZFZkDFs= 10 | github.com/smartystreets/assertions v1.2.0/go.mod h1:tcbTF8ujkAEcZ8TElKY+i30BzYlVhC/LOxJk7iOWnoo= 11 | github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= 12 | github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 13 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 14 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 15 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 16 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 17 | golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 18 | -------------------------------------------------------------------------------- /it.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | import ( 4 | "github.com/surrealdb/rixxdb/data" 5 | ) 6 | 7 | type IT struct { 8 | ok bool 9 | cu *data.Cursor 10 | } 11 | 12 | func (it *IT) First() ([]byte, *data.List) { 13 | if k, l := it.cu.First(); k != nil { 14 | return k, l 15 | } 16 | it.ok = false 17 | return nil, nil 18 | } 19 | 20 | func (it *IT) Last() ([]byte, *data.List) { 21 | if k, l := it.cu.Last(); k != nil { 22 | return k, l 23 | } 24 | it.ok = false 25 | return nil, nil 26 | } 27 | 28 | func (it *IT) Prev() ([]byte, *data.List) { 29 | if k, l := it.cu.Prev(); k != nil { 30 | return k, l 31 | } 32 | it.ok = false 33 | return nil, nil 34 | } 35 | 36 | func (it *IT) Next() ([]byte, *data.List) { 37 | if k, l := it.cu.Next(); k != nil { 38 | return k, l 39 | } 40 | it.ok = false 41 | return nil, nil 42 | } 43 | 44 | func (it *IT) Seek(key []byte) ([]byte, *data.List) { 45 | if k, l := it.cu.Seek(key); k != nil { 46 | return k, l 47 | } 48 | it.ok = false 49 | return nil, nil 50 | } 51 | 52 | func (it *IT) Valid() bool { 53 | return it.ok 54 | } 55 | -------------------------------------------------------------------------------- /kv.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | // KV represents a KV item stored in the database. 4 | type KV struct { 5 | ver uint64 6 | key []byte 7 | val []byte 8 | } 9 | 10 | // Exi returns whether this key-value item actually exists. 11 | func (kv *KV) Exi() bool { 12 | return kv.val != nil 13 | } 14 | 15 | // Key returns the key for the underlying key-value item. 16 | func (kv *KV) Key() []byte { 17 | return kv.key 18 | } 19 | 20 | // Val returns the value for the underlying key-value item. 21 | func (kv *KV) Val() []byte { 22 | return kv.val 23 | } 24 | 25 | // Ver returns the version for the underlying key-value item. 26 | func (kv *KV) Ver() uint64 { 27 | return kv.ver 28 | } 29 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | import ( 4 | "time" 5 | ) 6 | 7 | const ( 8 | // FlushNever is used to prevent syncing of data to disk. When 9 | // this option is specified, all data changes are kept in memory, 10 | // and the database is run with no durability. 11 | FlushNever time.Duration = -1 12 | // ShrinkNever is used to disable shrinking the data file. All 13 | // exact changes to the database are preserved with this option 14 | // but the data file can grow larger than the data stored. 15 | ShrinkNever time.Duration = -1 16 | ) 17 | 18 | // Config represents database configuration options. These 19 | // options are used to change various behaviors of the database. 20 | type Config struct { 21 | // SyncWrites specifies whether the file system should be forced to 22 | // flush it's buffers to disk using 'fsync'. When the go programme 23 | // exits, the operating system will eventually write the file changes 24 | // to disk. Setting SyncWrites to 'true' will guarantee that data is 25 | // flushed to persistent disk even if the system is powered down or 26 | // the operating system crashes. This is only used when transactions 27 | // are set to save to disk on commit by setting FlushPolicy to '0'. 28 | SyncWrites bool 29 | 30 | // FlushPolicy defines how often the data is synced to the append-only 31 | // file on disk. '-1' ensures that the database is kept in-memory 32 | // with no persistence, '0' ensures that the database is persisted 33 | // to disk after every commit, and a number greater than 0 ensures 34 | // that the database is committed to disk after the specified duration. 35 | FlushPolicy time.Duration 36 | 37 | // ShrinkPolicy defines how often the database append-only file is 38 | // compacted, removing redundant log entries. '0' ensures that the 39 | // database append-only file is never compacted, and a number greater 40 | // than 0 ensures the database is compacted after the specified duration. 41 | ShrinkPolicy time.Duration 42 | 43 | // EncryptionKey enables the ability to specify an encryption key 44 | // to be used when storing the input data in the underlying data tree. 45 | // If the encryption key is specified, it must be either 16, 24, or 46 | // 32 bytes in length in order to use AES-128, AES-192, or AES-256 47 | // respectively. If no encryption key is specified then the data 48 | // will not be encrypted before storage or when writing to disk. 49 | EncryptionKey []byte 50 | } 51 | -------------------------------------------------------------------------------- /main_test.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "sync" 7 | "testing" 8 | 9 | . "github.com/smartystreets/goconvey/convey" 10 | ) 11 | 12 | var test = []byte("test") 13 | 14 | func TestPersistence(t *testing.T) { 15 | 16 | var db *DB 17 | var err error 18 | 19 | Convey("No persistence", t, func() { 20 | db, err = Open("memory", &Config{EncryptionKey: nil}) 21 | So(err, ShouldBeNil) 22 | So(db, ShouldNotBeNil) 23 | So(db.Flush(), ShouldEqual, ErrDbMemoryOnly) 24 | So(db.Close(), ShouldBeNil) 25 | }) 26 | 27 | Convey("No persistence", t, func() { 28 | db, err = Open("memory", &Config{EncryptionKey: []byte("1234567890123456")}) 29 | So(err, ShouldBeNil) 30 | So(db, ShouldNotBeNil) 31 | So(db.Flush(), ShouldEqual, ErrDbMemoryOnly) 32 | So(db.Close(), ShouldBeNil) 33 | }) 34 | 35 | Convey("Invalid key", t, func() { 36 | db, err = Open("memory", &Config{EncryptionKey: []byte("12345678901234567890")}) 37 | So(err, ShouldEqual, ErrDbInvalidEncryptionKey) 38 | }) 39 | 40 | Convey("Path persistence", t, func() { 41 | db, err = Open("test.db", &Config{}) 42 | So(err, ShouldBeNil) 43 | So(db, ShouldNotBeNil) 44 | So(db.Flush(), ShouldBeNil) 45 | So(db.Close(), ShouldBeNil) 46 | db, err = Open("test.db", &Config{}) 47 | So(err, ShouldBeNil) 48 | So(db, ShouldNotBeNil) 49 | So(db.Close(), ShouldBeNil) 50 | os.RemoveAll("test.db") 51 | }) 52 | 53 | Convey("File persistence", t, func() { 54 | db, err = Open("file://test.db", &Config{}) 55 | So(err, ShouldBeNil) 56 | So(db, ShouldNotBeNil) 57 | So(db.Flush(), ShouldBeNil) 58 | So(db.Close(), ShouldBeNil) 59 | db, err = Open("file://test.db", &Config{}) 60 | So(err, ShouldBeNil) 61 | So(db, ShouldNotBeNil) 62 | So(db.Close(), ShouldBeNil) 63 | os.RemoveAll("test.db") 64 | }) 65 | 66 | } 67 | 68 | func TestTransactions(t *testing.T) { 69 | 70 | Convey("Check transaction errors", t, func() { 71 | 72 | var db *DB 73 | var tx *TX 74 | var err error 75 | 76 | db, err = Open("memory", &Config{}) 77 | 78 | tx, err = db.Begin(false) 79 | 80 | // Test writing to a read transaction 81 | 82 | _, err = tx.Clr(test) 83 | So(err, ShouldEqual, ErrTxNotWritable) 84 | _, err = tx.ClrL(test, 0) 85 | So(err, ShouldEqual, ErrTxNotWritable) 86 | _, err = tx.ClrP(test, 0) 87 | So(err, ShouldEqual, ErrTxNotWritable) 88 | _, err = tx.ClrR(test, test, 0) 89 | So(err, ShouldEqual, ErrTxNotWritable) 90 | 91 | _, err = tx.Del(0, test) 92 | So(err, ShouldEqual, ErrTxNotWritable) 93 | _, err = tx.DelC(0, test, nil) 94 | So(err, ShouldEqual, ErrTxNotWritable) 95 | _, err = tx.DelL(0, test, 0) 96 | So(err, ShouldEqual, ErrTxNotWritable) 97 | _, err = tx.DelP(0, test, 0) 98 | So(err, ShouldEqual, ErrTxNotWritable) 99 | _, err = tx.DelR(0, test, test, 0) 100 | So(err, ShouldEqual, ErrTxNotWritable) 101 | 102 | _, err = tx.Put(0, test, nil) 103 | So(err, ShouldEqual, ErrTxNotWritable) 104 | _, err = tx.PutC(0, test, nil, nil) 105 | So(err, ShouldEqual, ErrTxNotWritable) 106 | _, err = tx.PutL(0, test, nil, 0) 107 | So(err, ShouldEqual, ErrTxNotWritable) 108 | _, err = tx.PutP(0, test, nil, 0) 109 | So(err, ShouldEqual, ErrTxNotWritable) 110 | _, err = tx.PutR(0, test, test, nil, 0) 111 | So(err, ShouldEqual, ErrTxNotWritable) 112 | 113 | err = tx.Commit() 114 | So(err, ShouldEqual, ErrTxNotWritable) 115 | 116 | // Try altering a closed transaction 117 | 118 | err = tx.Cancel() 119 | So(err, ShouldEqual, ErrTxClosed) 120 | 121 | err = tx.Commit() 122 | So(err, ShouldEqual, ErrTxClosed) 123 | 124 | _, err = tx.Clr(test) 125 | So(err, ShouldEqual, ErrTxClosed) 126 | _, err = tx.ClrL(test, 0) 127 | So(err, ShouldEqual, ErrTxClosed) 128 | _, err = tx.ClrP(test, 0) 129 | So(err, ShouldEqual, ErrTxClosed) 130 | _, err = tx.ClrR(test, test, 0) 131 | So(err, ShouldEqual, ErrTxClosed) 132 | 133 | _, err = tx.Get(0, test) 134 | So(err, ShouldEqual, ErrTxClosed) 135 | _, err = tx.GetL(0, test, 0) 136 | So(err, ShouldEqual, ErrTxClosed) 137 | _, err = tx.GetP(0, test, 0) 138 | So(err, ShouldEqual, ErrTxClosed) 139 | _, err = tx.GetR(0, test, test, 0) 140 | So(err, ShouldEqual, ErrTxClosed) 141 | 142 | _, err = tx.Del(0, test) 143 | So(err, ShouldEqual, ErrTxClosed) 144 | _, err = tx.DelC(0, test, nil) 145 | So(err, ShouldEqual, ErrTxClosed) 146 | _, err = tx.DelL(0, test, 0) 147 | So(err, ShouldEqual, ErrTxClosed) 148 | _, err = tx.DelP(0, test, 0) 149 | So(err, ShouldEqual, ErrTxClosed) 150 | _, err = tx.DelR(0, test, test, 0) 151 | So(err, ShouldEqual, ErrTxClosed) 152 | 153 | _, err = tx.Put(0, test, test) 154 | So(err, ShouldEqual, ErrTxClosed) 155 | _, err = tx.PutC(0, test, test, nil) 156 | So(err, ShouldEqual, ErrTxClosed) 157 | _, err = tx.PutL(0, test, test, 0) 158 | So(err, ShouldEqual, ErrTxClosed) 159 | _, err = tx.PutP(0, test, test, 0) 160 | So(err, ShouldEqual, ErrTxClosed) 161 | _, err = tx.PutR(0, test, test, nil, 0) 162 | So(err, ShouldEqual, ErrTxClosed) 163 | 164 | tx.Cancel() 165 | 166 | // Try passing invalid arguments 167 | 168 | tx, err = db.Begin(true) 169 | 170 | _, err = tx.Clr(nil) 171 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 172 | _, err = tx.ClrL(nil, 0) 173 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 174 | _, err = tx.ClrP(nil, 0) 175 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 176 | _, err = tx.ClrR(nil, test, 0) 177 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 178 | _, err = tx.ClrR(test, nil, 0) 179 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 180 | 181 | _, err = tx.Get(0, nil) 182 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 183 | _, err = tx.GetL(0, nil, 0) 184 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 185 | _, err = tx.GetP(0, nil, 0) 186 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 187 | _, err = tx.GetR(0, nil, test, 0) 188 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 189 | _, err = tx.GetR(0, test, nil, 0) 190 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 191 | 192 | _, err = tx.Del(0, nil) 193 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 194 | _, err = tx.DelC(0, nil, nil) 195 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 196 | _, err = tx.DelL(0, nil, 0) 197 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 198 | _, err = tx.DelP(0, nil, 0) 199 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 200 | _, err = tx.DelR(0, nil, test, 0) 201 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 202 | _, err = tx.DelR(0, test, nil, 0) 203 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 204 | 205 | _, err = tx.Put(0, nil, test) 206 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 207 | _, err = tx.PutC(0, nil, test, nil) 208 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 209 | _, err = tx.PutL(0, nil, test, 0) 210 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 211 | _, err = tx.PutP(0, nil, test, 0) 212 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 213 | _, err = tx.PutR(0, nil, test, nil, 0) 214 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 215 | _, err = tx.PutR(0, test, nil, nil, 0) 216 | So(err, ShouldEqual, ErrTxKeyCanNotBeNil) 217 | 218 | tx.Cancel() 219 | 220 | // Test managed transaction errors 221 | 222 | db.View(func(tx *TX) error { 223 | return fmt.Errorf("Test error") 224 | }) 225 | 226 | db.Update(func(tx *TX) error { 227 | return fmt.Errorf("Test error") 228 | }) 229 | 230 | db.View(func(tx *TX) error { 231 | err = tx.Cancel() 232 | So(err, ShouldEqual, ErrTxNotEditable) 233 | err = tx.Commit() 234 | So(err, ShouldEqual, ErrTxNotEditable) 235 | return nil 236 | }) 237 | 238 | db.Update(func(tx *TX) error { 239 | err = tx.Cancel() 240 | So(err, ShouldEqual, ErrTxNotEditable) 241 | err = tx.Commit() 242 | So(err, ShouldEqual, ErrTxNotEditable) 243 | return nil 244 | }) 245 | 246 | // Test managed transaction panics 247 | 248 | func() { 249 | 250 | defer func() { recover() }() 251 | 252 | db.View(func(tx *TX) error { 253 | panic("test") 254 | }) 255 | 256 | }() 257 | 258 | func() { 259 | 260 | defer func() { recover() }() 261 | 262 | db.Update(func(tx *TX) error { 263 | panic("test") 264 | }) 265 | 266 | }() 267 | 268 | }) 269 | 270 | Convey("Check cancelling transactions", t, func() { 271 | 272 | var db *DB 273 | var tx *TX 274 | var kv *KV 275 | var ok bool 276 | var err error 277 | 278 | db, err = Open("memory", &Config{}) 279 | So(err, ShouldBeNil) 280 | 281 | tx, err = db.Begin(true) 282 | So(err, ShouldBeNil) 283 | So(tx, ShouldNotBeNil) 284 | 285 | ok = tx.Closed() 286 | So(ok, ShouldBeFalse) 287 | 288 | kv, err = tx.Put(0, []byte("test"), []byte("tester")) 289 | So(err, ShouldBeNil) 290 | So(kv, ShouldNotBeNil) 291 | 292 | kv, err = tx.Get(0, []byte("test")) 293 | So(err, ShouldBeNil) 294 | So(kv, ShouldNotBeNil) 295 | So(kv.Exi(), ShouldBeTrue) 296 | 297 | err = tx.Cancel() 298 | So(err, ShouldBeNil) 299 | 300 | ok = tx.Closed() 301 | So(ok, ShouldBeTrue) 302 | 303 | tx, err = db.Begin(false) 304 | So(err, ShouldBeNil) 305 | So(tx, ShouldNotBeNil) 306 | 307 | kv, err = tx.Get(0, []byte("test")) 308 | So(err, ShouldBeNil) 309 | So(kv, ShouldNotBeNil) 310 | So(kv.Exi(), ShouldBeFalse) 311 | 312 | err = tx.Cancel() 313 | So(err, ShouldBeNil) 314 | 315 | }) 316 | 317 | Convey("Check committing transactions", t, func() { 318 | 319 | var db *DB 320 | var tx *TX 321 | var kv *KV 322 | var ok bool 323 | var err error 324 | 325 | db, err = Open("memory", &Config{}) 326 | So(err, ShouldBeNil) 327 | 328 | tx, err = db.Begin(true) 329 | So(err, ShouldBeNil) 330 | So(tx, ShouldNotBeNil) 331 | 332 | ok = tx.Closed() 333 | So(ok, ShouldBeFalse) 334 | 335 | kv, err = tx.Put(0, []byte("test"), []byte("tester")) 336 | So(err, ShouldBeNil) 337 | So(kv, ShouldNotBeNil) 338 | 339 | kv, err = tx.Get(0, []byte("test")) 340 | So(err, ShouldBeNil) 341 | So(kv, ShouldNotBeNil) 342 | So(kv.Exi(), ShouldBeTrue) 343 | 344 | err = tx.Commit() 345 | So(err, ShouldBeNil) 346 | 347 | ok = tx.Closed() 348 | So(ok, ShouldBeTrue) 349 | 350 | tx, err = db.Begin(false) 351 | So(err, ShouldBeNil) 352 | So(tx, ShouldNotBeNil) 353 | 354 | kv, err = tx.Get(0, []byte("test")) 355 | So(err, ShouldBeNil) 356 | So(kv, ShouldNotBeNil) 357 | So(kv.Exi(), ShouldBeTrue) 358 | So(kv.Ver(), ShouldEqual, 0) 359 | So(kv.Key(), ShouldResemble, []byte("test")) 360 | So(kv.Val(), ShouldResemble, []byte("tester")) 361 | 362 | err = tx.Cancel() 363 | So(err, ShouldBeNil) 364 | 365 | }) 366 | 367 | Convey("Write many keys concurrently", t, func() { 368 | 369 | var db *DB 370 | var tx *TX 371 | var kv *KV 372 | var ok bool 373 | var err error 374 | var max = 100 375 | 376 | db, err = Open("memory", &Config{}) 377 | So(err, ShouldBeNil) 378 | 379 | tx, err = db.Begin(true) 380 | So(err, ShouldBeNil) 381 | So(tx, ShouldNotBeNil) 382 | 383 | ok = tx.Closed() 384 | So(ok, ShouldBeFalse) 385 | 386 | var w sync.WaitGroup 387 | 388 | w.Add(3) 389 | 390 | go func() { 391 | for i := 1; i < max; i++ { 392 | tx.Put(0, []byte(fmt.Sprint(i)), []byte(fmt.Sprint(i))) 393 | } 394 | w.Done() 395 | }() 396 | 397 | go func() { 398 | for i := 1; i < max; i++ { 399 | tx.Put(0, []byte(fmt.Sprint(i)), []byte(fmt.Sprint(i))) 400 | } 401 | w.Done() 402 | }() 403 | 404 | go func() { 405 | for i := 1; i < max; i++ { 406 | tx.Put(0, []byte(fmt.Sprint(i)), []byte(fmt.Sprint(i))) 407 | } 408 | w.Done() 409 | }() 410 | 411 | w.Wait() 412 | 413 | err = tx.Commit() 414 | So(err, ShouldBeNil) 415 | 416 | tx, err = db.Begin(false) 417 | So(err, ShouldBeNil) 418 | So(tx, ShouldNotBeNil) 419 | 420 | for i := 1; i < max; i++ { 421 | kv, err = tx.Get(0, []byte(fmt.Sprint(i))) 422 | So(err, ShouldBeNil) 423 | So(kv, ShouldNotBeNil) 424 | So(kv.Exi(), ShouldBeTrue) 425 | So(kv.Val(), ShouldResemble, []byte(fmt.Sprint(i))) 426 | } 427 | 428 | err = tx.Cancel() 429 | So(err, ShouldBeNil) 430 | 431 | }) 432 | 433 | } 434 | 435 | func TestOperations(t *testing.T) { 436 | 437 | var db *DB 438 | var tx *TX 439 | var kv *KV 440 | var kvs []*KV 441 | var err error 442 | 443 | Convey("Check complex iterations", t, func() { 444 | 445 | db, err = Open("memory", &Config{}) 446 | 447 | tx, err = db.Begin(true) 448 | So(err, ShouldBeNil) 449 | So(tx, ShouldNotBeNil) 450 | 451 | defer tx.Cancel() 452 | 453 | _, err = tx.Put(5, []byte("/kv"), []byte("KV")) 454 | So(err, ShouldBeNil) 455 | _, err = tx.Put(5, []byte("/kv/ns"), []byte("NS")) 456 | So(err, ShouldBeNil) 457 | _, err = tx.Put(5, []byte("/kv/ns/db"), []byte("DB")) 458 | So(err, ShouldBeNil) 459 | _, err = tx.Put(5, []byte("/kv/ns/db/tb1"), []byte("TB1")) 460 | So(err, ShouldBeNil) 461 | _, err = tx.Put(5, []byte("/kv/ns/db/tb2"), []byte("TB2")) 462 | So(err, ShouldBeNil) 463 | _, err = tx.Put(5, []byte("/kv/ns/db/tb3"), []byte("TB3")) 464 | So(err, ShouldBeNil) 465 | 466 | kv, err = tx.Get(10, []byte("/kv")) 467 | So(kv.Val(), ShouldResemble, []byte("KV")) 468 | kv, err = tx.Get(10, []byte("/kv/ns")) 469 | So(kv.Val(), ShouldResemble, []byte("NS")) 470 | kv, err = tx.Get(10, []byte("/kv/ns/db")) 471 | So(kv.Val(), ShouldResemble, []byte("DB")) 472 | 473 | kvs, err = tx.GetL(10, []byte("/"), 0) 474 | So(kvs, ShouldHaveLength, 1) 475 | So(kvs[0].Val(), ShouldResemble, []byte("KV")) 476 | 477 | kvs, err = tx.GetL(10, []byte("/kv"), 0) 478 | So(kvs, ShouldHaveLength, 1) 479 | So(kvs[0].Val(), ShouldResemble, []byte("NS")) 480 | 481 | kvs, err = tx.GetL(10, []byte("/kv/ns"), 0) 482 | So(kvs, ShouldHaveLength, 1) 483 | So(kvs[0].Val(), ShouldResemble, []byte("DB")) 484 | 485 | kvs, err = tx.GetL(10, []byte("/kv/ns/db"), 0) 486 | So(kvs, ShouldHaveLength, 3) 487 | So(kvs[0].Val(), ShouldResemble, []byte("TB1")) 488 | So(kvs[1].Val(), ShouldResemble, []byte("TB2")) 489 | So(kvs[2].Val(), ShouldResemble, []byte("TB3")) 490 | 491 | kvs, err = tx.GetP(10, []byte("/"), 0) 492 | So(kvs, ShouldHaveLength, 6) 493 | So(kvs[0].Val(), ShouldResemble, []byte("KV")) 494 | So(kvs[1].Val(), ShouldResemble, []byte("NS")) 495 | So(kvs[2].Val(), ShouldResemble, []byte("DB")) 496 | So(kvs[3].Val(), ShouldResemble, []byte("TB1")) 497 | So(kvs[4].Val(), ShouldResemble, []byte("TB2")) 498 | So(kvs[5].Val(), ShouldResemble, []byte("TB3")) 499 | 500 | kvs, err = tx.GetP(10, []byte("/k"), 0) 501 | So(kvs, ShouldHaveLength, 6) 502 | So(kvs[0].Val(), ShouldResemble, []byte("KV")) 503 | So(kvs[1].Val(), ShouldResemble, []byte("NS")) 504 | So(kvs[2].Val(), ShouldResemble, []byte("DB")) 505 | So(kvs[3].Val(), ShouldResemble, []byte("TB1")) 506 | So(kvs[4].Val(), ShouldResemble, []byte("TB2")) 507 | So(kvs[5].Val(), ShouldResemble, []byte("TB3")) 508 | 509 | kvs, err = tx.GetP(10, []byte("/kv"), 0) 510 | So(kvs, ShouldHaveLength, 6) 511 | So(kvs[0].Val(), ShouldResemble, []byte("KV")) 512 | So(kvs[1].Val(), ShouldResemble, []byte("NS")) 513 | So(kvs[2].Val(), ShouldResemble, []byte("DB")) 514 | So(kvs[3].Val(), ShouldResemble, []byte("TB1")) 515 | So(kvs[4].Val(), ShouldResemble, []byte("TB2")) 516 | So(kvs[5].Val(), ShouldResemble, []byte("TB3")) 517 | 518 | kvs, err = tx.GetP(10, []byte("/kv/ns/db"), 0) 519 | So(kvs, ShouldHaveLength, 4) 520 | So(kvs[0].Val(), ShouldResemble, []byte("DB")) 521 | So(kvs[1].Val(), ShouldResemble, []byte("TB1")) 522 | So(kvs[2].Val(), ShouldResemble, []byte("TB2")) 523 | So(kvs[3].Val(), ShouldResemble, []byte("TB3")) 524 | 525 | kvs, err = tx.GetP(10, []byte("/kv/ns/db/"), 0) 526 | So(kvs, ShouldHaveLength, 3) 527 | So(kvs[0].Val(), ShouldResemble, []byte("TB1")) 528 | So(kvs[1].Val(), ShouldResemble, []byte("TB2")) 529 | So(kvs[2].Val(), ShouldResemble, []byte("TB3")) 530 | 531 | kvs, err = tx.GetP(10, []byte("/kv/ns/db/tb"), 0) 532 | So(kvs, ShouldHaveLength, 3) 533 | So(kvs[0].Val(), ShouldResemble, []byte("TB1")) 534 | So(kvs[1].Val(), ShouldResemble, []byte("TB2")) 535 | So(kvs[2].Val(), ShouldResemble, []byte("TB3")) 536 | 537 | kvs, err = tx.GetR(10, []byte("/"), []byte("/kv/ns/db/tb~"), 0) 538 | So(kvs, ShouldHaveLength, 6) 539 | So(kvs[0].Val(), ShouldResemble, []byte("KV")) 540 | So(kvs[1].Val(), ShouldResemble, []byte("NS")) 541 | So(kvs[2].Val(), ShouldResemble, []byte("DB")) 542 | So(kvs[3].Val(), ShouldResemble, []byte("TB1")) 543 | So(kvs[4].Val(), ShouldResemble, []byte("TB2")) 544 | So(kvs[5].Val(), ShouldResemble, []byte("TB3")) 545 | 546 | kvs, err = tx.GetR(10, []byte("/kv/ns/db/tb~"), []byte("/"), 0) 547 | So(kvs, ShouldHaveLength, 6) 548 | So(kvs[0].Val(), ShouldResemble, []byte("TB3")) 549 | So(kvs[1].Val(), ShouldResemble, []byte("TB2")) 550 | So(kvs[2].Val(), ShouldResemble, []byte("TB1")) 551 | So(kvs[3].Val(), ShouldResemble, []byte("DB")) 552 | So(kvs[4].Val(), ShouldResemble, []byte("NS")) 553 | So(kvs[5].Val(), ShouldResemble, []byte("KV")) 554 | 555 | kvs, err = tx.GetR(10, []byte("/~"), []byte("/"), 0) 556 | So(kvs, ShouldHaveLength, 6) 557 | So(kvs[0].Val(), ShouldResemble, []byte("TB3")) 558 | So(kvs[1].Val(), ShouldResemble, []byte("TB2")) 559 | So(kvs[2].Val(), ShouldResemble, []byte("TB1")) 560 | So(kvs[3].Val(), ShouldResemble, []byte("DB")) 561 | So(kvs[4].Val(), ShouldResemble, []byte("NS")) 562 | So(kvs[5].Val(), ShouldResemble, []byte("KV")) 563 | 564 | kvs, err = tx.GetR(10, []byte("/kv/ns/db/tb"), []byte("/kv/ns/db/tb~"), 0) 565 | So(kvs, ShouldHaveLength, 3) 566 | So(kvs[0].Val(), ShouldResemble, []byte("TB1")) 567 | So(kvs[1].Val(), ShouldResemble, []byte("TB2")) 568 | So(kvs[2].Val(), ShouldResemble, []byte("TB3")) 569 | 570 | kvs, err = tx.GetR(10, []byte("/kv/ns/db/tb~"), []byte("/kv/ns/db/tb"), 0) 571 | So(kvs, ShouldHaveLength, 3) 572 | So(kvs[0].Val(), ShouldResemble, []byte("TB3")) 573 | So(kvs[1].Val(), ShouldResemble, []byte("TB2")) 574 | So(kvs[2].Val(), ShouldResemble, []byte("TB1")) 575 | 576 | kv, err = tx.PutC(15, []byte("/kv/ns/db/tb1"), []byte("TB-1"), []byte("TB1")) 577 | So(kv.Val(), ShouldResemble, []byte("TB1")) 578 | 579 | kv, err = tx.PutC(15, []byte("/kv/ns/db/tb2"), []byte("TB-2"), []byte("TB2")) 580 | So(kv.Val(), ShouldResemble, []byte("TB2")) 581 | 582 | kv, err = tx.PutC(15, []byte("/kv/ns/db/tb3"), []byte("TB-3"), []byte("TB3")) 583 | So(kv.Val(), ShouldResemble, []byte("TB3")) 584 | 585 | kv, err = tx.PutC(15, []byte("/kv/ns/db/tb1"), []byte("TB-4"), []byte("TB4")) 586 | So(err, ShouldEqual, ErrTxNotExpectedValue) 587 | 588 | kv, err = tx.PutC(15, []byte("/kv/ns/db/tb4"), []byte("TB-4"), nil) 589 | So(kv.Val(), ShouldResemble, []byte(nil)) 590 | 591 | kv, err = tx.PutC(15, []byte("/kv/ns/db/tb5"), []byte("TB-5"), nil) 592 | So(kv.Val(), ShouldResemble, []byte(nil)) 593 | 594 | kvs, err = tx.PutL(20, []byte("/"), []byte("KV-test"), 0) 595 | So(kvs, ShouldHaveLength, 1) 596 | So(kvs[0].Key(), ShouldResemble, []byte("/kv")) 597 | So(kvs[0].Val(), ShouldResemble, []byte("KV")) 598 | 599 | kvs, err = tx.PutL(20, []byte("/kv"), []byte("NS-test"), 0) 600 | So(kvs, ShouldHaveLength, 1) 601 | So(kvs[0].Key(), ShouldResemble, []byte("/kv/ns")) 602 | So(kvs[0].Val(), ShouldResemble, []byte("NS")) 603 | 604 | kvs, err = tx.PutL(20, []byte("/kv/ns"), []byte("DB-test"), 0) 605 | So(kvs, ShouldHaveLength, 1) 606 | So(kvs[0].Key(), ShouldResemble, []byte("/kv/ns/db")) 607 | So(kvs[0].Val(), ShouldResemble, []byte("DB")) 608 | 609 | kvs, err = tx.PutL(25, []byte("/kv/ns/db"), []byte("TB-test"), 0) 610 | So(kvs, ShouldHaveLength, 5) 611 | So(kvs[0].Key(), ShouldResemble, []byte("/kv/ns/db/tb1")) 612 | So(kvs[0].Val(), ShouldResemble, []byte("TB-1")) 613 | So(kvs[1].Key(), ShouldResemble, []byte("/kv/ns/db/tb2")) 614 | So(kvs[1].Val(), ShouldResemble, []byte("TB-2")) 615 | So(kvs[2].Key(), ShouldResemble, []byte("/kv/ns/db/tb3")) 616 | So(kvs[2].Val(), ShouldResemble, []byte("TB-3")) 617 | So(kvs[3].Key(), ShouldResemble, []byte("/kv/ns/db/tb4")) 618 | So(kvs[3].Val(), ShouldResemble, []byte("TB-4")) 619 | So(kvs[4].Key(), ShouldResemble, []byte("/kv/ns/db/tb5")) 620 | So(kvs[4].Val(), ShouldResemble, []byte("TB-5")) 621 | 622 | kvs, err = tx.DelL(25, []byte("/kv/ns/db"), 0) 623 | So(kvs, ShouldHaveLength, 5) 624 | So(kvs[0].Val(), ShouldResemble, []byte("TB-test")) 625 | So(kvs[1].Val(), ShouldResemble, []byte("TB-test")) 626 | So(kvs[2].Val(), ShouldResemble, []byte("TB-test")) 627 | So(kvs[3].Val(), ShouldResemble, []byte("TB-test")) 628 | So(kvs[4].Val(), ShouldResemble, []byte("TB-test")) 629 | 630 | kvs, err = tx.GetP(30, []byte("/"), 0) 631 | So(kvs, ShouldHaveLength, 3) 632 | So(kvs[0].Val(), ShouldResemble, []byte("KV-test")) 633 | So(kvs[1].Val(), ShouldResemble, []byte("NS-test")) 634 | So(kvs[2].Val(), ShouldResemble, []byte("DB-test")) 635 | 636 | kvs, err = tx.DelP(30, []byte("/"), 0) 637 | So(kvs, ShouldHaveLength, 8) 638 | So(kvs[0].Val(), ShouldResemble, []byte("KV-test")) 639 | So(kvs[1].Val(), ShouldResemble, []byte("NS-test")) 640 | So(kvs[2].Val(), ShouldResemble, []byte("DB-test")) 641 | So(kvs[3].Val(), ShouldResemble, []byte(nil)) 642 | So(kvs[4].Val(), ShouldResemble, []byte(nil)) 643 | So(kvs[5].Val(), ShouldResemble, []byte(nil)) 644 | So(kvs[6].Val(), ShouldResemble, []byte(nil)) 645 | So(kvs[7].Val(), ShouldResemble, []byte(nil)) 646 | 647 | kvs, err = tx.GetP(30, []byte("/"), 0) 648 | So(kvs, ShouldHaveLength, 0) 649 | 650 | kvs, err = tx.PutP(35, []byte("/"), []byte("TEST"), 0) 651 | So(kvs, ShouldHaveLength, 8) 652 | So(kvs[0].Val(), ShouldResemble, []byte(nil)) 653 | So(kvs[1].Val(), ShouldResemble, []byte(nil)) 654 | So(kvs[2].Val(), ShouldResemble, []byte(nil)) 655 | So(kvs[3].Val(), ShouldResemble, []byte(nil)) 656 | So(kvs[4].Val(), ShouldResemble, []byte(nil)) 657 | So(kvs[5].Val(), ShouldResemble, []byte(nil)) 658 | So(kvs[6].Val(), ShouldResemble, []byte(nil)) 659 | So(kvs[7].Val(), ShouldResemble, []byte(nil)) 660 | 661 | kvs, err = tx.DelP(35, []byte("/"), 0) 662 | So(kvs, ShouldHaveLength, 8) 663 | So(kvs[0].Val(), ShouldResemble, []byte("TEST")) 664 | So(kvs[1].Val(), ShouldResemble, []byte("TEST")) 665 | So(kvs[2].Val(), ShouldResemble, []byte("TEST")) 666 | So(kvs[3].Val(), ShouldResemble, []byte("TEST")) 667 | So(kvs[4].Val(), ShouldResemble, []byte("TEST")) 668 | So(kvs[5].Val(), ShouldResemble, []byte("TEST")) 669 | So(kvs[6].Val(), ShouldResemble, []byte("TEST")) 670 | So(kvs[7].Val(), ShouldResemble, []byte("TEST")) 671 | 672 | kvs, err = tx.PutR(35, []byte("/"), []byte("/kv/ns/db/tb~"), []byte("TEMP"), 0) 673 | So(kvs, ShouldHaveLength, 8) 674 | So(kvs[0].Val(), ShouldResemble, []byte(nil)) 675 | So(kvs[1].Val(), ShouldResemble, []byte(nil)) 676 | So(kvs[2].Val(), ShouldResemble, []byte(nil)) 677 | So(kvs[3].Val(), ShouldResemble, []byte(nil)) 678 | So(kvs[4].Val(), ShouldResemble, []byte(nil)) 679 | So(kvs[5].Val(), ShouldResemble, []byte(nil)) 680 | So(kvs[6].Val(), ShouldResemble, []byte(nil)) 681 | So(kvs[7].Val(), ShouldResemble, []byte(nil)) 682 | 683 | kvs, err = tx.DelR(35, []byte("/"), []byte("/kv/ns/db/tb~"), 0) 684 | So(kvs, ShouldHaveLength, 8) 685 | So(kvs[0].Val(), ShouldResemble, []byte("TEMP")) 686 | So(kvs[1].Val(), ShouldResemble, []byte("TEMP")) 687 | So(kvs[2].Val(), ShouldResemble, []byte("TEMP")) 688 | So(kvs[3].Val(), ShouldResemble, []byte("TEMP")) 689 | So(kvs[4].Val(), ShouldResemble, []byte("TEMP")) 690 | So(kvs[5].Val(), ShouldResemble, []byte("TEMP")) 691 | So(kvs[6].Val(), ShouldResemble, []byte("TEMP")) 692 | So(kvs[7].Val(), ShouldResemble, []byte("TEMP")) 693 | 694 | kvs, err = tx.PutR(35, []byte("/kv/ns/db/tb~"), []byte("/"), []byte("TEMP"), 0) 695 | So(kvs, ShouldHaveLength, 8) 696 | So(kvs[0].Val(), ShouldResemble, []byte(nil)) 697 | So(kvs[1].Val(), ShouldResemble, []byte(nil)) 698 | So(kvs[2].Val(), ShouldResemble, []byte(nil)) 699 | So(kvs[3].Val(), ShouldResemble, []byte(nil)) 700 | So(kvs[4].Val(), ShouldResemble, []byte(nil)) 701 | So(kvs[5].Val(), ShouldResemble, []byte(nil)) 702 | So(kvs[6].Val(), ShouldResemble, []byte(nil)) 703 | So(kvs[7].Val(), ShouldResemble, []byte(nil)) 704 | 705 | kvs, err = tx.DelR(35, []byte("/kv/ns/db/tb~"), []byte("/"), 0) 706 | So(kvs, ShouldHaveLength, 8) 707 | So(kvs[0].Val(), ShouldResemble, []byte("TEMP")) 708 | So(kvs[1].Val(), ShouldResemble, []byte("TEMP")) 709 | So(kvs[2].Val(), ShouldResemble, []byte("TEMP")) 710 | So(kvs[3].Val(), ShouldResemble, []byte("TEMP")) 711 | So(kvs[4].Val(), ShouldResemble, []byte("TEMP")) 712 | So(kvs[5].Val(), ShouldResemble, []byte("TEMP")) 713 | So(kvs[6].Val(), ShouldResemble, []byte("TEMP")) 714 | So(kvs[7].Val(), ShouldResemble, []byte("TEMP")) 715 | 716 | kvs, err = tx.GetP(35, []byte("/"), 0) 717 | So(kvs, ShouldHaveLength, 0) 718 | 719 | kv, err = tx.Put(40, []byte("/kv"), []byte("KV-test")) 720 | So(kv.Val(), ShouldResemble, []byte(nil)) 721 | 722 | kv, err = tx.Get(40, []byte("/kv")) 723 | So(kv.Val(), ShouldResemble, []byte("KV-test")) 724 | 725 | kv, err = tx.Del(40, []byte("/kv")) 726 | So(kv.Val(), ShouldResemble, []byte("KV-test")) 727 | 728 | kv, err = tx.Put(40, []byte("/kv"), []byte("KV-test")) 729 | So(kv.Val(), ShouldResemble, []byte(nil)) 730 | 731 | kv, err = tx.DelC(40, []byte("/kv"), []byte("KV-tester")) 732 | So(err, ShouldResemble, ErrTxNotExpectedValue) 733 | 734 | kv, err = tx.DelC(40, []byte("/kv"), []byte("KV-test")) 735 | So(kv.Val(), ShouldResemble, []byte("KV-test")) 736 | 737 | kv, err = tx.Get(40, []byte("/kv")) 738 | So(kv.Val(), ShouldResemble, []byte(nil)) 739 | 740 | kv, err = tx.Put(45, []byte("/kv/ns/db/tbx"), []byte("TBX")) 741 | So(kv.Val(), ShouldResemble, []byte(nil)) 742 | 743 | kv, err = tx.Get(45, []byte("/kv/ns/db/tbx")) 744 | So(kv.Val(), ShouldResemble, []byte("TBX")) 745 | 746 | kv, err = tx.Clr([]byte("/kv/ns/db/tbx")) 747 | So(kv.Val(), ShouldResemble, []byte("TBX")) 748 | 749 | kv, err = tx.Put(45, []byte("/kv/ns/db/tbx"), []byte("TBX")) 750 | So(kv.Val(), ShouldResemble, []byte(nil)) 751 | 752 | kv, err = tx.Put(45, []byte("/kv/ns/db/tbx"), []byte("TBX")) 753 | So(kv.Val(), ShouldResemble, []byte("TBX")) 754 | 755 | kv, err = tx.Put(45, []byte("/kv/ns/db/tbx"), []byte("TBX")) 756 | So(kv.Val(), ShouldResemble, []byte("TBX")) 757 | 758 | kvs, err = tx.ClrL([]byte("/kv/ns"), 0) 759 | So(kvs, ShouldHaveLength, 1) 760 | So(kvs[0].Val(), ShouldResemble, []byte(nil)) 761 | 762 | kvs, err = tx.ClrP([]byte("/kv/ns/db/tb"), 0) 763 | So(kvs, ShouldHaveLength, 6) 764 | So(kvs[0].Val(), ShouldResemble, []byte(nil)) 765 | So(kvs[1].Val(), ShouldResemble, []byte(nil)) 766 | So(kvs[2].Val(), ShouldResemble, []byte(nil)) 767 | 768 | kvs, err = tx.ClrP([]byte("/"), 0) 769 | So(kvs, ShouldHaveLength, 2) 770 | So(kvs[0].Val(), ShouldResemble, []byte(nil)) 771 | 772 | _, err = tx.Put(5, []byte("/kv"), []byte("KV")) 773 | So(err, ShouldBeNil) 774 | _, err = tx.Put(5, []byte("/kv/ns"), []byte("NS")) 775 | So(err, ShouldBeNil) 776 | _, err = tx.Put(5, []byte("/kv/ns/db"), []byte("DB")) 777 | So(err, ShouldBeNil) 778 | _, err = tx.Put(5, []byte("/kv/ns/db/tb1"), []byte("TB1")) 779 | So(err, ShouldBeNil) 780 | _, err = tx.Put(5, []byte("/kv/ns/db/tb2"), []byte("TB2")) 781 | So(err, ShouldBeNil) 782 | _, err = tx.Put(5, []byte("/kv/ns/db/tb3"), []byte("TB3")) 783 | So(err, ShouldBeNil) 784 | 785 | kvs, err = tx.ClrR([]byte("/"), []byte("/kv/ns/db/tb~"), 0) 786 | So(kvs, ShouldHaveLength, 6) 787 | So(kvs[0].Val(), ShouldResemble, []byte("KV")) 788 | So(kvs[1].Val(), ShouldResemble, []byte("NS")) 789 | So(kvs[2].Val(), ShouldResemble, []byte("DB")) 790 | So(kvs[3].Val(), ShouldResemble, []byte("TB1")) 791 | So(kvs[4].Val(), ShouldResemble, []byte("TB2")) 792 | So(kvs[5].Val(), ShouldResemble, []byte("TB3")) 793 | 794 | _, err = tx.Put(5, []byte("/kv"), []byte("KV")) 795 | So(err, ShouldBeNil) 796 | _, err = tx.Put(5, []byte("/kv/ns"), []byte("NS")) 797 | So(err, ShouldBeNil) 798 | _, err = tx.Put(5, []byte("/kv/ns/db"), []byte("DB")) 799 | So(err, ShouldBeNil) 800 | _, err = tx.Put(5, []byte("/kv/ns/db/tb1"), []byte("TB1")) 801 | So(err, ShouldBeNil) 802 | _, err = tx.Put(5, []byte("/kv/ns/db/tb2"), []byte("TB2")) 803 | So(err, ShouldBeNil) 804 | _, err = tx.Put(5, []byte("/kv/ns/db/tb3"), []byte("TB3")) 805 | So(err, ShouldBeNil) 806 | 807 | kvs, err = tx.ClrR([]byte("/kv/ns/db/tb~"), []byte("/"), 0) 808 | So(kvs, ShouldHaveLength, 6) 809 | So(kvs[0].Val(), ShouldResemble, []byte("TB3")) 810 | So(kvs[1].Val(), ShouldResemble, []byte("TB2")) 811 | So(kvs[2].Val(), ShouldResemble, []byte("TB1")) 812 | So(kvs[3].Val(), ShouldResemble, []byte("DB")) 813 | So(kvs[4].Val(), ShouldResemble, []byte("NS")) 814 | So(kvs[5].Val(), ShouldResemble, []byte("KV")) 815 | 816 | So(tx.Commit(), ShouldBeNil) 817 | 818 | }) 819 | 820 | } 821 | -------------------------------------------------------------------------------- /op.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | const ( 4 | clr int8 = iota 5 | del 6 | put 7 | ) 8 | 9 | type op struct { 10 | op int8 11 | ver uint64 12 | key []byte 13 | val []byte 14 | } 15 | -------------------------------------------------------------------------------- /tx.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "io" 7 | "math" 8 | "sync" 9 | "sync/atomic" 10 | "unsafe" 11 | 12 | "github.com/surrealdb/rixxdb/data" 13 | ) 14 | 15 | // TX represents a transaction for modifying the contents of the database. 16 | // A TX is not safe to use from multiple goroutines concurrently. A TX 17 | // emits the events `cancel` and `commit` when the transaction is 18 | // cancelled and committed respectively. 19 | type TX struct { 20 | db *DB 21 | rw bool 22 | fn bool 23 | ops []*op 24 | tree *data.Copy 25 | lock sync.RWMutex 26 | } 27 | 28 | // Closed returns whether the transaction has been closed. 29 | func (tx *TX) Closed() bool { 30 | 31 | return (tx.db == nil) 32 | 33 | } 34 | 35 | // Cancel cancels the transaction. 36 | func (tx *TX) Cancel() error { 37 | 38 | if tx.fn { 39 | return ErrTxNotEditable 40 | } 41 | 42 | return tx.cancel() 43 | 44 | } 45 | 46 | // Commit commits the transaction. 47 | func (tx *TX) Commit() error { 48 | 49 | if tx.fn { 50 | return ErrTxNotEditable 51 | } 52 | 53 | return tx.commit() 54 | 55 | } 56 | 57 | func (tx *TX) cancel() error { 58 | 59 | defer func() { 60 | tx.db, tx.ops, tx.tree = nil, nil, nil 61 | }() 62 | 63 | // If this transaction no longer has 64 | // a tree then it has already been 65 | // closed, so return an error. 66 | 67 | if tx.db == nil { 68 | return ErrTxClosed 69 | } 70 | 71 | // If this transaction is not a 72 | // writable transaction then we can 73 | // cancell immediately without error. 74 | 75 | if tx.rw == false { 76 | return nil 77 | } 78 | 79 | // If this transaction is writable 80 | // then unlock the mutex so other 81 | // write transactions can be created. 82 | 83 | tx.db.lock.Unlock() 84 | 85 | return nil 86 | 87 | } 88 | 89 | func (tx *TX) commit() error { 90 | 91 | defer func() { 92 | tx.db, tx.ops, tx.tree = nil, nil, nil 93 | }() 94 | 95 | // If this transaction no longer has 96 | // a tree then it has already been 97 | // closed, so return an error. 98 | 99 | if tx.db == nil { 100 | return ErrTxClosed 101 | } 102 | 103 | // If this transaction is not a 104 | // writable transaction then we can 105 | // not commit, so return an error. 106 | 107 | if tx.rw == false { 108 | return ErrTxNotWritable 109 | } 110 | 111 | // If this transaction is writable 112 | // then unlock the mutex so other 113 | // write transactions can be created. 114 | 115 | defer tx.db.lock.Unlock() 116 | 117 | // Pass the transaction operations 118 | // to the database so that it can 119 | // write them to persistent storage. 120 | 121 | if err := tx.db.push(tx.ops); err != nil { 122 | return err 123 | } 124 | 125 | // If the transaction successfully 126 | // synced with the database file 127 | // thenn swap in the new tree. 128 | 129 | atomic.StorePointer(&tx.db.tree, unsafe.Pointer(tx.tree.Tree())) 130 | 131 | return nil 132 | 133 | } 134 | 135 | func (tx *TX) forced() error { 136 | 137 | defer func() { 138 | tx.db, tx.ops, tx.tree = nil, nil, nil 139 | }() 140 | 141 | // If this transaction no longer has 142 | // a tree then it has already been 143 | // closed, so return an error. 144 | 145 | if tx.db == nil { 146 | return ErrTxClosed 147 | } 148 | 149 | // If this transaction is not a 150 | // writable transaction then we can 151 | // not commit, so return an error. 152 | 153 | if tx.rw == false { 154 | return ErrTxNotWritable 155 | } 156 | 157 | // If this transaction is writable 158 | // then unlock the mutex so other 159 | // write transactions can be created. 160 | 161 | defer tx.db.lock.Unlock() 162 | 163 | // If the transaction successfully 164 | // synced with the database file 165 | // then swap in the new tree. 166 | 167 | atomic.StorePointer(&tx.db.tree, unsafe.Pointer(tx.tree.Tree())) 168 | 169 | return nil 170 | 171 | } 172 | 173 | // All retrieves a single key:value item. 174 | func (tx *TX) Cursor() *IT { 175 | return &IT{ 176 | ok: true, cu: tx.tree.Cursor(), 177 | } 178 | } 179 | 180 | // All retrieves a single key:value item. 181 | func (tx *TX) All(key []byte) (kvs []*KV, err error) { 182 | 183 | var v []byte 184 | 185 | if tx.db == nil { 186 | return nil, ErrTxClosed 187 | } 188 | 189 | if key == nil { 190 | return nil, ErrTxKeyCanNotBeNil 191 | } 192 | 193 | tx.lock.RLock() 194 | defer tx.lock.RUnlock() 195 | 196 | if l := tx.tree.All(key); l != nil { 197 | l.Walk(func(i *data.Item) bool { 198 | if v, err = tx.dec(i.Val()); err != nil { 199 | return true 200 | } 201 | kvs = append(kvs, &KV{ver: i.Ver(), key: key, val: v}) 202 | return false 203 | }) 204 | } 205 | 206 | return 207 | 208 | } 209 | 210 | // AllL retrieves the range of rows which are prefixed with `key`. 211 | func (tx *TX) AllL(key []byte, max uint64) (kvs []*KV, err error) { 212 | 213 | var v []byte 214 | 215 | if max == 0 { 216 | max = math.MaxUint64 217 | } 218 | 219 | if tx.db == nil { 220 | return nil, ErrTxClosed 221 | } 222 | 223 | if key == nil { 224 | return nil, ErrTxKeyCanNotBeNil 225 | } 226 | 227 | tx.lock.RLock() 228 | defer tx.lock.RUnlock() 229 | 230 | tx.tree.Root().Subs(key, func(k []byte, l *data.List) (e bool) { 231 | l.Walk(func(i *data.Item) bool { 232 | if v, err = tx.dec(i.Val()); err != nil { 233 | return true 234 | } 235 | kvs = append(kvs, &KV{ver: i.Ver(), key: key, val: v}) 236 | return false 237 | }) 238 | max-- 239 | return 240 | }) 241 | 242 | return 243 | 244 | } 245 | 246 | // AllP retrieves the range of rows which are prefixed with `key`. 247 | func (tx *TX) AllP(key []byte, max uint64) (kvs []*KV, err error) { 248 | 249 | var v []byte 250 | 251 | if max == 0 { 252 | max = math.MaxUint64 253 | } 254 | 255 | if tx.db == nil { 256 | return nil, ErrTxClosed 257 | } 258 | 259 | if key == nil { 260 | return nil, ErrTxKeyCanNotBeNil 261 | } 262 | 263 | tx.lock.RLock() 264 | defer tx.lock.RUnlock() 265 | 266 | c := tx.tree.Cursor() 267 | 268 | for k, l := c.Seek(key); max > 0 && bytes.HasPrefix(k, key); k, l = c.Next() { 269 | l.Walk(func(i *data.Item) bool { 270 | if v, err = tx.dec(i.Val()); err != nil { 271 | return true 272 | } 273 | kvs = append(kvs, &KV{ver: i.Ver(), key: key, val: v}) 274 | return false 275 | }) 276 | max-- 277 | } 278 | 279 | return 280 | 281 | } 282 | 283 | // AllR retrieves the range of `max` rows between `beg` (inclusive) and 284 | // `end` (exclusive). To return the range in descending order, ensure 285 | // that `end` sorts lower than `beg` in the key value store. 286 | func (tx *TX) AllR(beg, end []byte, max uint64) (kvs []*KV, err error) { 287 | 288 | var v []byte 289 | 290 | if max == 0 { 291 | max = math.MaxUint64 292 | } 293 | 294 | if tx.db == nil { 295 | return nil, ErrTxClosed 296 | } 297 | 298 | if beg == nil || end == nil { 299 | return nil, ErrTxKeyCanNotBeNil 300 | } 301 | 302 | tx.lock.RLock() 303 | defer tx.lock.RUnlock() 304 | 305 | c := tx.tree.Cursor() 306 | 307 | d := bytes.Compare(beg, end) 308 | 309 | switch { 310 | 311 | case d <= 0: 312 | 313 | for k, l := c.Seek(beg); max > 0 && k != nil && bytes.Compare(k, end) < 0; k, l = c.Next() { 314 | l.Walk(func(i *data.Item) bool { 315 | if v, err = tx.dec(i.Val()); err != nil { 316 | return true 317 | } 318 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 319 | return false 320 | }) 321 | max-- 322 | } 323 | 324 | case d >= 1: 325 | 326 | for k, l := c.Seek(beg); max > 0 && k != nil && bytes.Compare(end, k) < 0; k, l = c.Prev() { 327 | l.Walk(func(i *data.Item) bool { 328 | if v, err = tx.dec(i.Val()); err != nil { 329 | return true 330 | } 331 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 332 | return false 333 | }) 334 | max-- 335 | } 336 | 337 | } 338 | 339 | return 340 | 341 | } 342 | 343 | // Clr removes a single key:value item. 344 | func (tx *TX) Clr(key []byte) (kv *KV, err error) { 345 | 346 | var v []byte 347 | 348 | if tx.db == nil { 349 | return nil, ErrTxClosed 350 | } 351 | 352 | if !tx.rw { 353 | return nil, ErrTxNotWritable 354 | } 355 | 356 | if key == nil { 357 | return nil, ErrTxKeyCanNotBeNil 358 | } 359 | 360 | tx.lock.Lock() 361 | defer tx.lock.Unlock() 362 | 363 | kv = &KV{key: key} 364 | 365 | if i := tx.tree.Cut(key); i != nil { 366 | if v, err = tx.dec(i.Val()); err != nil { 367 | return nil, err 368 | } 369 | kv.ver, kv.val = i.Ver(), v 370 | } 371 | 372 | tx.clr(key) 373 | 374 | return 375 | 376 | } 377 | 378 | // ClrL removes the range of rows which are prefixed with `key`. 379 | func (tx *TX) ClrL(key []byte, max uint64) (kvs []*KV, err error) { 380 | 381 | var v []byte 382 | 383 | if max == 0 { 384 | max = math.MaxUint64 385 | } 386 | 387 | if tx.db == nil { 388 | return nil, ErrTxClosed 389 | } 390 | 391 | if !tx.rw { 392 | return nil, ErrTxNotWritable 393 | } 394 | 395 | if key == nil { 396 | return nil, ErrTxKeyCanNotBeNil 397 | } 398 | 399 | tx.lock.Lock() 400 | defer tx.lock.Unlock() 401 | 402 | tx.tree.Root().Subs(key, func(k []byte, l *data.List) (e bool) { 403 | if i := l.Max(); i != nil { 404 | if v, err = tx.dec(i.Val()); err != nil { 405 | return true 406 | } 407 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 408 | tx.tree.Cut(k) 409 | tx.clr(k) 410 | max-- 411 | } 412 | return 413 | }) 414 | 415 | return 416 | 417 | } 418 | 419 | // ClrP removes the range of rows which are prefixed with `key`. 420 | func (tx *TX) ClrP(key []byte, max uint64) (kvs []*KV, err error) { 421 | 422 | var v []byte 423 | 424 | if max == 0 { 425 | max = math.MaxUint64 426 | } 427 | 428 | if tx.db == nil { 429 | return nil, ErrTxClosed 430 | } 431 | 432 | if !tx.rw { 433 | return nil, ErrTxNotWritable 434 | } 435 | 436 | if key == nil { 437 | return nil, ErrTxKeyCanNotBeNil 438 | } 439 | 440 | tx.lock.Lock() 441 | defer tx.lock.Unlock() 442 | 443 | c := tx.tree.Cursor() 444 | 445 | for k, l := c.Seek(key); max > 0 && k != nil && bytes.HasPrefix(k, key); k, l = c.Next() { 446 | if i := l.Max(); i != nil { 447 | if v, err = tx.dec(i.Val()); err != nil { 448 | return nil, err 449 | } 450 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 451 | tx.tree.Cut(k) 452 | tx.clr(k) 453 | max-- 454 | } 455 | } 456 | 457 | return 458 | 459 | } 460 | 461 | // ClrR removes the range of `max` rows between `beg` (inclusive) and 462 | // `end` (exclusive). To return the range in descending order, ensure 463 | // that `end` sorts lower than `beg` in the key value store. 464 | func (tx *TX) ClrR(beg, end []byte, max uint64) (kvs []*KV, err error) { 465 | 466 | var v []byte 467 | 468 | if max == 0 { 469 | max = math.MaxUint64 470 | } 471 | 472 | if tx.db == nil { 473 | return nil, ErrTxClosed 474 | } 475 | 476 | if !tx.rw { 477 | return nil, ErrTxNotWritable 478 | } 479 | 480 | if beg == nil || end == nil { 481 | return nil, ErrTxKeyCanNotBeNil 482 | } 483 | 484 | tx.lock.Lock() 485 | defer tx.lock.Unlock() 486 | 487 | c := tx.tree.Cursor() 488 | 489 | d := bytes.Compare(beg, end) 490 | 491 | switch { 492 | 493 | case d <= 0: 494 | 495 | for k, l := c.Seek(beg); max > 0 && k != nil && bytes.Compare(k, end) < 0; k, l = c.Next() { 496 | if i := l.Max(); i != nil { 497 | if v, err = tx.dec(i.Val()); err != nil { 498 | return nil, err 499 | } 500 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 501 | tx.tree.Cut(k) 502 | tx.clr(k) 503 | max-- 504 | } 505 | } 506 | 507 | case d >= 1: 508 | 509 | k, l := c.Seek(beg) 510 | if k == nil { 511 | k, l = c.Last() 512 | } 513 | 514 | for ; max > 0 && k != nil && bytes.Compare(end, k) < 0; k, l = c.Prev() { 515 | if i := l.Max(); i != nil { 516 | if v, err = tx.dec(i.Val()); err != nil { 517 | return nil, err 518 | } 519 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 520 | tx.tree.Cut(k) 521 | tx.clr(k) 522 | max-- 523 | } 524 | } 525 | 526 | } 527 | 528 | return 529 | 530 | } 531 | 532 | // Get retrieves a single key:value item. 533 | func (tx *TX) Get(ver uint64, key []byte) (kv *KV, err error) { 534 | 535 | var v []byte 536 | 537 | if tx.db == nil { 538 | return nil, ErrTxClosed 539 | } 540 | 541 | if key == nil { 542 | return nil, ErrTxKeyCanNotBeNil 543 | } 544 | 545 | tx.lock.RLock() 546 | defer tx.lock.RUnlock() 547 | 548 | kv = &KV{key: key} 549 | 550 | if i := tx.tree.Get(ver, key); i != nil { 551 | if v, err = tx.dec(i.Val()); err != nil { 552 | return nil, err 553 | } 554 | kv.ver, kv.val = i.Ver(), v 555 | } 556 | 557 | return 558 | 559 | } 560 | 561 | // GetL retrieves the range of rows which are prefixed with `key`. 562 | func (tx *TX) GetL(ver uint64, key []byte, max uint64) (kvs []*KV, err error) { 563 | 564 | var v []byte 565 | 566 | if max == 0 { 567 | max = math.MaxUint64 568 | } 569 | 570 | if tx.db == nil { 571 | return nil, ErrTxClosed 572 | } 573 | 574 | if key == nil { 575 | return nil, ErrTxKeyCanNotBeNil 576 | } 577 | 578 | tx.lock.RLock() 579 | defer tx.lock.RUnlock() 580 | 581 | tx.tree.Root().Subs(key, func(k []byte, l *data.List) (e bool) { 582 | if i := l.Get(ver, data.Upto); i != nil && i.Val() != nil { 583 | if v, err = tx.dec(i.Val()); err != nil { 584 | return true 585 | } 586 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 587 | max-- 588 | } 589 | return 590 | }) 591 | 592 | return 593 | 594 | } 595 | 596 | // GetP retrieves the range of rows which are prefixed with `key`. 597 | func (tx *TX) GetP(ver uint64, key []byte, max uint64) (kvs []*KV, err error) { 598 | 599 | var v []byte 600 | 601 | if max == 0 { 602 | max = math.MaxUint64 603 | } 604 | 605 | if tx.db == nil { 606 | return nil, ErrTxClosed 607 | } 608 | 609 | if key == nil { 610 | return nil, ErrTxKeyCanNotBeNil 611 | } 612 | 613 | tx.lock.RLock() 614 | defer tx.lock.RUnlock() 615 | 616 | c := tx.tree.Cursor() 617 | 618 | for k, l := c.Seek(key); max > 0 && k != nil && bytes.HasPrefix(k, key); k, l = c.Next() { 619 | if i := l.Get(ver, data.Upto); i != nil && i.Val() != nil { 620 | if v, err = tx.dec(i.Val()); err != nil { 621 | return nil, err 622 | } 623 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 624 | max-- 625 | } 626 | } 627 | 628 | return 629 | 630 | } 631 | 632 | // GetR retrieves the range of `max` rows between `beg` (inclusive) and 633 | // `end` (exclusive). To return the range in descending order, ensure 634 | // that `end` sorts lower than `beg` in the key value store. 635 | func (tx *TX) GetR(ver uint64, beg, end []byte, max uint64) (kvs []*KV, err error) { 636 | 637 | var v []byte 638 | 639 | if max == 0 { 640 | max = math.MaxUint64 641 | } 642 | 643 | if tx.db == nil { 644 | return nil, ErrTxClosed 645 | } 646 | 647 | if beg == nil || end == nil { 648 | return nil, ErrTxKeyCanNotBeNil 649 | } 650 | 651 | tx.lock.RLock() 652 | defer tx.lock.RUnlock() 653 | 654 | c := tx.tree.Cursor() 655 | 656 | d := bytes.Compare(beg, end) 657 | 658 | switch { 659 | 660 | case d <= 0: 661 | 662 | for k, l := c.Seek(beg); max > 0 && k != nil && bytes.Compare(k, end) < 0; k, l = c.Next() { 663 | if i := l.Get(ver, data.Upto); i != nil && i.Val() != nil { 664 | if v, err = tx.dec(i.Val()); err != nil { 665 | return nil, err 666 | } 667 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 668 | max-- 669 | } 670 | } 671 | 672 | case d >= 1: 673 | 674 | k, l := c.Seek(beg) 675 | if k == nil { 676 | k, l = c.Last() 677 | } 678 | 679 | for ; max > 0 && k != nil && bytes.Compare(end, k) < 0; k, l = c.Prev() { 680 | if i := l.Get(ver, data.Upto); i != nil && i.Val() != nil { 681 | if v, err = tx.dec(i.Val()); err != nil { 682 | return nil, err 683 | } 684 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 685 | max-- 686 | } 687 | } 688 | 689 | } 690 | 691 | return 692 | 693 | } 694 | 695 | // Del deletes a single key:value item. 696 | func (tx *TX) Del(ver uint64, key []byte) (kv *KV, err error) { 697 | 698 | var v []byte 699 | 700 | if tx.db == nil { 701 | return nil, ErrTxClosed 702 | } 703 | 704 | if !tx.rw { 705 | return nil, ErrTxNotWritable 706 | } 707 | 708 | if key == nil { 709 | return nil, ErrTxKeyCanNotBeNil 710 | } 711 | 712 | tx.lock.Lock() 713 | defer tx.lock.Unlock() 714 | 715 | kv = &KV{key: key} 716 | 717 | if i := tx.tree.Del(ver, key); i != nil { 718 | if v, err = tx.dec(i.Val()); err != nil { 719 | return nil, err 720 | } 721 | kv.ver, kv.val = i.Ver(), v 722 | } 723 | 724 | tx.del(ver, key) 725 | 726 | return 727 | 728 | } 729 | 730 | // DelC conditionally deletes a key if the existing value is equal to the 731 | // expected value. 732 | func (tx *TX) DelC(ver uint64, key, exp []byte) (kv *KV, err error) { 733 | 734 | var v []byte 735 | 736 | if tx.db == nil { 737 | return nil, ErrTxClosed 738 | } 739 | 740 | if !tx.rw { 741 | return nil, ErrTxNotWritable 742 | } 743 | 744 | if key == nil { 745 | return nil, ErrTxKeyCanNotBeNil 746 | } 747 | 748 | tx.lock.Lock() 749 | defer tx.lock.Unlock() 750 | 751 | var now []byte 752 | 753 | // Get the item at the key 754 | 755 | if i := tx.tree.Get(ver, key); i != nil { 756 | if now, err = tx.dec(i.Val()); err != nil { 757 | return nil, err 758 | } 759 | } 760 | 761 | // Check if the values match 762 | 763 | if !alter(now, exp) { 764 | return nil, ErrTxNotExpectedValue 765 | } 766 | 767 | kv = &KV{key: key} 768 | 769 | if i := tx.tree.Del(ver, key); i != nil { 770 | if v, err = tx.dec(i.Val()); err != nil { 771 | return nil, err 772 | } 773 | kv.ver, kv.val = i.Ver(), v 774 | } 775 | 776 | tx.del(ver, key) 777 | 778 | return 779 | 780 | } 781 | 782 | // DelL deletes the range of rows which are prefixed with `key`. 783 | func (tx *TX) DelL(ver uint64, key []byte, max uint64) (kvs []*KV, err error) { 784 | 785 | var v []byte 786 | 787 | if max == 0 { 788 | max = math.MaxUint64 789 | } 790 | 791 | if tx.db == nil { 792 | return nil, ErrTxClosed 793 | } 794 | 795 | if !tx.rw { 796 | return nil, ErrTxNotWritable 797 | } 798 | 799 | if key == nil { 800 | return nil, ErrTxKeyCanNotBeNil 801 | } 802 | 803 | tx.lock.Lock() 804 | defer tx.lock.Unlock() 805 | 806 | tx.tree.Root().Subs(key, func(k []byte, l *data.List) (e bool) { 807 | if i := l.Put(ver, nil); i != nil { 808 | if v, err = tx.dec(i.Val()); err != nil { 809 | return true 810 | } 811 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 812 | tx.del(ver, k) 813 | max-- 814 | } 815 | return 816 | }) 817 | 818 | return 819 | 820 | } 821 | 822 | // DelP deletes the range of rows which are prefixed with `key`. 823 | func (tx *TX) DelP(ver uint64, key []byte, max uint64) (kvs []*KV, err error) { 824 | 825 | var v []byte 826 | 827 | if max == 0 { 828 | max = math.MaxUint64 829 | } 830 | 831 | if tx.db == nil { 832 | return nil, ErrTxClosed 833 | } 834 | 835 | if !tx.rw { 836 | return nil, ErrTxNotWritable 837 | } 838 | 839 | if key == nil { 840 | return nil, ErrTxKeyCanNotBeNil 841 | } 842 | 843 | tx.lock.Lock() 844 | defer tx.lock.Unlock() 845 | 846 | c := tx.tree.Cursor() 847 | 848 | for k, l := c.Seek(key); max > 0 && k != nil && bytes.HasPrefix(k, key); k, l = c.Next() { 849 | if i := l.Put(ver, nil); i != nil { 850 | if v, err = tx.dec(i.Val()); err != nil { 851 | return nil, err 852 | } 853 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 854 | tx.del(ver, k) 855 | max-- 856 | } 857 | } 858 | 859 | return 860 | 861 | } 862 | 863 | // DelR deletes the range of `max` rows between `beg` (inclusive) and 864 | // `end` (exclusive). To delete the range in descending order, ensure 865 | // that `end` sorts lower than `beg` in the key value store. 866 | func (tx *TX) DelR(ver uint64, beg, end []byte, max uint64) (kvs []*KV, err error) { 867 | 868 | var v []byte 869 | 870 | if max == 0 { 871 | max = math.MaxUint64 872 | } 873 | 874 | if tx.db == nil { 875 | return nil, ErrTxClosed 876 | } 877 | 878 | if !tx.rw { 879 | return nil, ErrTxNotWritable 880 | } 881 | 882 | if beg == nil || end == nil { 883 | return nil, ErrTxKeyCanNotBeNil 884 | } 885 | 886 | tx.lock.Lock() 887 | defer tx.lock.Unlock() 888 | 889 | c := tx.tree.Cursor() 890 | 891 | d := bytes.Compare(beg, end) 892 | 893 | switch { 894 | 895 | case d <= 0: 896 | 897 | for k, l := c.Seek(beg); max > 0 && k != nil && bytes.Compare(k, end) < 0; k, l = c.Next() { 898 | if i := l.Put(ver, nil); i != nil { 899 | if v, err = tx.dec(i.Val()); err != nil { 900 | return nil, err 901 | } 902 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 903 | tx.del(ver, k) 904 | max-- 905 | } 906 | } 907 | 908 | case d >= 1: 909 | 910 | k, l := c.Seek(beg) 911 | if k == nil { 912 | k, l = c.Last() 913 | } 914 | 915 | for ; max > 0 && k != nil && bytes.Compare(end, k) < 0; k, l = c.Prev() { 916 | if i := l.Put(ver, nil); i != nil { 917 | if v, err = tx.dec(i.Val()); err != nil { 918 | return nil, err 919 | } 920 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 921 | tx.del(ver, k) 922 | max-- 923 | } 924 | } 925 | 926 | } 927 | 928 | return 929 | 930 | } 931 | 932 | // Put sets the value for a key. 933 | func (tx *TX) Put(ver uint64, key, val []byte) (kv *KV, err error) { 934 | 935 | var v []byte 936 | 937 | if tx.db == nil { 938 | return nil, ErrTxClosed 939 | } 940 | 941 | if !tx.rw { 942 | return nil, ErrTxNotWritable 943 | } 944 | 945 | if key == nil { 946 | return nil, ErrTxKeyCanNotBeNil 947 | } 948 | 949 | if val, err = tx.enc(val); err != nil { 950 | return nil, err 951 | } 952 | 953 | tx.lock.Lock() 954 | defer tx.lock.Unlock() 955 | 956 | kv = &KV{key: key} 957 | 958 | if i := tx.tree.Put(ver, key, val); i != nil { 959 | if v, err = tx.dec(i.Val()); err != nil { 960 | return nil, err 961 | } 962 | kv.ver, kv.val = i.Ver(), v 963 | } 964 | 965 | tx.put(ver, key, val) 966 | 967 | return 968 | 969 | } 970 | 971 | // PutC conditionally sets the value for a key if the existing value is 972 | // equal to the expected value. To conditionally set a value only if there 973 | // is no existing entry pass nil for the expected value. 974 | func (tx *TX) PutC(ver uint64, key, val, exp []byte) (kv *KV, err error) { 975 | 976 | var v []byte 977 | 978 | if tx.db == nil { 979 | return nil, ErrTxClosed 980 | } 981 | 982 | if !tx.rw { 983 | return nil, ErrTxNotWritable 984 | } 985 | 986 | if key == nil { 987 | return nil, ErrTxKeyCanNotBeNil 988 | } 989 | 990 | if val, err = tx.enc(val); err != nil { 991 | return nil, err 992 | } 993 | 994 | tx.lock.Lock() 995 | defer tx.lock.Unlock() 996 | 997 | var now []byte 998 | 999 | // Get the item at the key 1000 | 1001 | if i := tx.tree.Get(ver, key); i != nil { 1002 | if now, err = tx.dec(i.Val()); err != nil { 1003 | return nil, err 1004 | } 1005 | } 1006 | 1007 | // Check if the values match 1008 | 1009 | if !check(now, exp) { 1010 | return nil, ErrTxNotExpectedValue 1011 | } 1012 | 1013 | kv = &KV{key: key} 1014 | 1015 | if i := tx.tree.Put(ver, key, val); i != nil { 1016 | if v, err = tx.dec(i.Val()); err != nil { 1017 | return nil, err 1018 | } 1019 | kv.ver, kv.val = i.Ver(), v 1020 | } 1021 | 1022 | tx.put(ver, key, val) 1023 | 1024 | return 1025 | 1026 | } 1027 | 1028 | // PutL updates the range of rows which are prefixed with `key`. 1029 | func (tx *TX) PutL(ver uint64, key, val []byte, max uint64) (kvs []*KV, err error) { 1030 | 1031 | var v []byte 1032 | 1033 | if max == 0 { 1034 | max = math.MaxUint64 1035 | } 1036 | 1037 | if tx.db == nil { 1038 | return nil, ErrTxClosed 1039 | } 1040 | 1041 | if !tx.rw { 1042 | return nil, ErrTxNotWritable 1043 | } 1044 | 1045 | if key == nil { 1046 | return nil, ErrTxKeyCanNotBeNil 1047 | } 1048 | 1049 | if val, err = tx.enc(val); err != nil { 1050 | return nil, err 1051 | } 1052 | 1053 | tx.lock.Lock() 1054 | defer tx.lock.Unlock() 1055 | 1056 | tx.tree.Root().Subs(key, func(k []byte, l *data.List) (e bool) { 1057 | if i := l.Put(ver, val); i != nil { 1058 | if v, err = tx.dec(i.Val()); err != nil { 1059 | return true 1060 | } 1061 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 1062 | tx.put(ver, k, val) 1063 | max-- 1064 | } 1065 | return 1066 | }) 1067 | 1068 | return 1069 | 1070 | } 1071 | 1072 | // PutP updates the range of rows which are prefixed with `key`. 1073 | func (tx *TX) PutP(ver uint64, key, val []byte, max uint64) (kvs []*KV, err error) { 1074 | 1075 | var v []byte 1076 | 1077 | if max == 0 { 1078 | max = math.MaxUint64 1079 | } 1080 | 1081 | if tx.db == nil { 1082 | return nil, ErrTxClosed 1083 | } 1084 | 1085 | if !tx.rw { 1086 | return nil, ErrTxNotWritable 1087 | } 1088 | 1089 | if key == nil { 1090 | return nil, ErrTxKeyCanNotBeNil 1091 | } 1092 | 1093 | if val, err = tx.enc(val); err != nil { 1094 | return nil, err 1095 | } 1096 | 1097 | tx.lock.Lock() 1098 | defer tx.lock.Unlock() 1099 | 1100 | c := tx.tree.Cursor() 1101 | 1102 | for k, l := c.Seek(key); max > 0 && k != nil && bytes.HasPrefix(k, key); k, l = c.Next() { 1103 | if i := l.Put(ver, val); i != nil { 1104 | if v, err = tx.dec(i.Val()); err != nil { 1105 | return nil, err 1106 | } 1107 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 1108 | tx.put(ver, k, val) 1109 | max-- 1110 | } 1111 | } 1112 | 1113 | return 1114 | 1115 | } 1116 | 1117 | // PutR updates the range of `max` rows between `beg` (inclusive) and 1118 | // `end` (exclusive). To delete the range in descending order, ensure 1119 | // that `end` sorts lower than `beg` in the key value store. 1120 | func (tx *TX) PutR(ver uint64, beg, end, val []byte, max uint64) (kvs []*KV, err error) { 1121 | 1122 | var v []byte 1123 | 1124 | if max == 0 { 1125 | max = math.MaxUint64 1126 | } 1127 | 1128 | if tx.db == nil { 1129 | return nil, ErrTxClosed 1130 | } 1131 | 1132 | if !tx.rw { 1133 | return nil, ErrTxNotWritable 1134 | } 1135 | 1136 | if beg == nil || end == nil { 1137 | return nil, ErrTxKeyCanNotBeNil 1138 | } 1139 | 1140 | if val, err = tx.enc(val); err != nil { 1141 | return nil, err 1142 | } 1143 | 1144 | tx.lock.Lock() 1145 | defer tx.lock.Unlock() 1146 | 1147 | c := tx.tree.Cursor() 1148 | 1149 | d := bytes.Compare(beg, end) 1150 | 1151 | switch { 1152 | 1153 | case d <= 0: 1154 | 1155 | for k, l := c.Seek(beg); max > 0 && k != nil && bytes.Compare(k, end) < 0; k, l = c.Next() { 1156 | if i := l.Put(ver, val); i != nil { 1157 | if v, err = tx.dec(i.Val()); err != nil { 1158 | return nil, err 1159 | } 1160 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 1161 | tx.put(ver, k, val) 1162 | max-- 1163 | } 1164 | } 1165 | 1166 | case d >= 1: 1167 | 1168 | k, l := c.Seek(beg) 1169 | if k == nil { 1170 | k, l = c.Last() 1171 | } 1172 | 1173 | for ; max > 0 && k != nil && bytes.Compare(end, k) < 0; k, l = c.Prev() { 1174 | if i := l.Put(ver, val); i != nil { 1175 | if v, err = tx.dec(i.Val()); err != nil { 1176 | return nil, err 1177 | } 1178 | kvs = append(kvs, &KV{ver: i.Ver(), key: k, val: v}) 1179 | tx.put(ver, k, val) 1180 | max-- 1181 | } 1182 | } 1183 | 1184 | } 1185 | 1186 | return 1187 | 1188 | } 1189 | 1190 | // ---------------------------------------------------------------------- 1191 | 1192 | func (tx *TX) dec(src []byte) (dst []byte, err error) { 1193 | if dst, err = decrypt(tx.db.conf.EncryptionKey, src); err != nil { 1194 | return nil, ErrDbInvalidEncryptionKey 1195 | } 1196 | return 1197 | } 1198 | 1199 | func (tx *TX) enc(src []byte) (dst []byte, err error) { 1200 | if dst, err = encrypt(tx.db.conf.EncryptionKey, src); err != nil { 1201 | return nil, ErrDbInvalidEncryptionKey 1202 | } 1203 | return 1204 | } 1205 | 1206 | func (tx *TX) put(ver uint64, key, val []byte) { 1207 | 1208 | if tx.db.file.pntr == nil { 1209 | return 1210 | } 1211 | 1212 | tx.ops = append(tx.ops, &op{ 1213 | op: put, ver: ver, key: key, val: val, 1214 | }) 1215 | 1216 | } 1217 | 1218 | func (tx *TX) del(ver uint64, key []byte) { 1219 | 1220 | if tx.db.file.pntr == nil { 1221 | return 1222 | } 1223 | 1224 | tx.ops = append(tx.ops, &op{ 1225 | op: del, ver: ver, key: key, 1226 | }) 1227 | 1228 | } 1229 | 1230 | func (tx *TX) clr(key []byte) { 1231 | 1232 | if tx.db.file.pntr == nil { 1233 | return 1234 | } 1235 | 1236 | tx.ops = append(tx.ops, &op{ 1237 | op: clr, key: key, 1238 | }) 1239 | 1240 | } 1241 | 1242 | func (tx *TX) out(ver uint64, key, val []byte) (out []byte) { 1243 | 1244 | out = append(out, 'P') 1245 | out = append(out, wver(ver)...) 1246 | out = append(out, wlen(key)...) 1247 | out = append(out, key...) 1248 | out = append(out, wlen(val)...) 1249 | out = append(out, val...) 1250 | out = append(out, '\n') 1251 | 1252 | return 1253 | 1254 | } 1255 | 1256 | func (tx *TX) inj(r io.Reader) error { 1257 | 1258 | b := bufio.NewReaderSize(r, 100*1024*1024) 1259 | 1260 | for { 1261 | 1262 | var bit byte 1263 | var err error 1264 | var ver uint64 1265 | var key []byte 1266 | var val []byte 1267 | 1268 | if bit, err = rbit(b); err == io.EOF { 1269 | break 1270 | } 1271 | 1272 | switch bit { 1273 | 1274 | case '\n': 1275 | continue 1276 | 1277 | case 'E': 1278 | 1279 | tx.tree = data.New().Copy() 1280 | 1281 | continue 1282 | 1283 | case 'C': 1284 | 1285 | if key, err = rkey(b); err != nil { 1286 | return err 1287 | } 1288 | 1289 | tx.tree.Cut(key) 1290 | 1291 | continue 1292 | 1293 | case 'D': 1294 | 1295 | if ver, err = rint(b); err != nil { 1296 | return err 1297 | } 1298 | if key, err = rkey(b); err != nil { 1299 | return err 1300 | } 1301 | 1302 | tx.tree.Del(ver, key) 1303 | 1304 | continue 1305 | 1306 | case 'P': 1307 | 1308 | if ver, err = rint(b); err != nil { 1309 | return err 1310 | } 1311 | if key, err = rkey(b); err != nil { 1312 | return err 1313 | } 1314 | if val, err = rval(b); err != nil { 1315 | return err 1316 | } 1317 | 1318 | if len(val) == 0 { 1319 | val = nil 1320 | } 1321 | 1322 | tx.tree.Put(ver, key, val) 1323 | 1324 | continue 1325 | 1326 | } 1327 | 1328 | return ErrDbFileContentsInvalid 1329 | 1330 | } 1331 | 1332 | return tx.forced() 1333 | 1334 | } 1335 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package rixxdb 2 | 3 | import ( 4 | "bufio" 5 | "bytes" 6 | "crypto/aes" 7 | "crypto/cipher" 8 | "crypto/rand" 9 | "encoding/binary" 10 | "errors" 11 | "io" 12 | ) 13 | 14 | var chars = []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") 15 | 16 | // Used to see if we can conditionally put a value. We can only put 17 | // a value if the value is the same, or if both items are nil. 18 | func check(a, b []byte) bool { 19 | if a != nil && b != nil { 20 | return bytes.Equal(a, b) 21 | } else if a == nil && b == nil { 22 | return true 23 | } 24 | return false 25 | } 26 | 27 | // Used to see if we can conditionally del a value. We can only del 28 | // a value if the value is the same, and neither item is nil. 29 | func alter(a, b []byte) bool { 30 | if a != nil && b != nil { 31 | return bytes.Equal(a, b) 32 | } else if a == nil && b == nil { 33 | return false 34 | } 35 | return false 36 | } 37 | 38 | func encrypt(key []byte, src []byte) (dst []byte, err error) { 39 | 40 | if key == nil || len(key) == 0 || len(src) == 0 { 41 | return src, nil 42 | } 43 | 44 | // Initiate AES 45 | block, _ := aes.NewCipher(key) 46 | 47 | // Initiate cipher 48 | cipher, _ := cipher.NewGCM(block) 49 | 50 | // Initiate nonce 51 | nonce := random(12) 52 | 53 | dst = cipher.Seal(nil, nonce, src, nil) 54 | 55 | dst = append(nonce[:], dst[:]...) 56 | 57 | return 58 | 59 | } 60 | 61 | func decrypt(key []byte, src []byte) (dst []byte, err error) { 62 | 63 | if key == nil || len(key) == 0 || len(src) == 0 { 64 | return src, nil 65 | } 66 | 67 | // Corrupt 68 | if len(src) < 12 { 69 | return src, errors.New("Invalid data") 70 | } 71 | 72 | // Initiate AES 73 | block, _ := aes.NewCipher(key) 74 | 75 | // Initiate cipher 76 | cipher, _ := cipher.NewGCM(block) 77 | 78 | return cipher.Open(nil, src[:12], src[12:], nil) 79 | 80 | } 81 | 82 | func random(l int) []byte { 83 | 84 | if l == 0 { 85 | return nil 86 | } 87 | 88 | i := 0 89 | t := len(chars) 90 | m := 255 - (256 % t) 91 | b := make([]byte, l) 92 | r := make([]byte, l+(l/4)) 93 | 94 | for { 95 | 96 | rand.Read(r) 97 | 98 | for _, rb := range r { 99 | c := int(rb) 100 | if c > m { 101 | continue 102 | } 103 | b[i] = chars[c%t] 104 | i++ 105 | if i == l { 106 | return b 107 | } 108 | } 109 | 110 | } 111 | 112 | } 113 | 114 | func wver(v uint64) (bit []byte) { 115 | 116 | bit = make([]byte, 8) 117 | 118 | binary.BigEndian.PutUint64(bit, uint64(v)) 119 | 120 | return 121 | } 122 | 123 | func wlen(v []byte) (bit []byte) { 124 | 125 | bit = make([]byte, 8) 126 | 127 | binary.BigEndian.PutUint64(bit, uint64(len(v))) 128 | 129 | return 130 | } 131 | 132 | func rbit(r *bufio.Reader) (byte, error) { 133 | 134 | return r.ReadByte() 135 | 136 | } 137 | 138 | func rint(b *bufio.Reader) (uint64, error) { 139 | 140 | v := make([]byte, 8) 141 | 142 | _, err := io.ReadFull(b, v) 143 | 144 | return binary.BigEndian.Uint64(v), err 145 | 146 | } 147 | 148 | func rkey(b *bufio.Reader) ([]byte, error) { 149 | 150 | l, err := rint(b) 151 | if err != nil { 152 | return nil, err 153 | } 154 | 155 | v := make([]byte, int(l)) 156 | 157 | _, err = io.ReadFull(b, v) 158 | 159 | return v, err 160 | 161 | } 162 | 163 | func rval(b *bufio.Reader) ([]byte, error) { 164 | 165 | l, err := rint(b) 166 | if err != nil { 167 | return nil, err 168 | } 169 | 170 | v := make([]byte, int(l)) 171 | 172 | _, err = io.ReadFull(b, v) 173 | 174 | return v, err 175 | 176 | } 177 | --------------------------------------------------------------------------------