├── .gitignore ├── .golangci.yml ├── CLA ├── LICENSE ├── Makefile ├── README.md ├── btree.go ├── btree_api.go ├── btree_balance_test.go ├── btree_cache_test.go ├── btree_common_api_test.go ├── btree_dump.go ├── btree_specific_api_test.go ├── btree_validate.go ├── common_api.go ├── common_api_test.go ├── compare_test.go ├── go.mod ├── go.sum ├── llrb_common_api_test.go ├── llrb_dump.go ├── llrb_specific_api_test.go ├── llrb_tree.go ├── llrb_tree_api.go └── llrb_validate.go /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .vscode 3 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | linters: 2 | disable-all: true 3 | enable: 4 | - asciicheck 5 | - bodyclose 6 | - canonicalheader 7 | - copyloopvar 8 | - depguard 9 | - dogsled 10 | - dupl 11 | - durationcheck 12 | - errcheck 13 | - fatcontext 14 | - gci 15 | - gochecksumtype 16 | - gocritic 17 | - gofmt 18 | - goheader 19 | - goimports 20 | - gomoddirectives 21 | - goprintffuncname 22 | - gosec 23 | - gosimple 24 | - govet 25 | - importas 26 | - ineffassign 27 | - intrange 28 | - misspell 29 | - nilnesserr 30 | - nolintlint 31 | - perfsprint 32 | - prealloc 33 | - protogetter 34 | - reassign 35 | - revive 36 | - staticcheck 37 | # - testpackage 38 | - typecheck 39 | - unconvert 40 | - unparam 41 | - unused 42 | - whitespace 43 | 44 | # See: https://golangci-lint.run/usage/linters/ 45 | linters-settings: 46 | dogsled: 47 | max-blank-identifiers: 3 48 | dupl: 49 | threshold: 10000 50 | revive: 51 | ignore-generated-header: true 52 | severity: warning 53 | rules: 54 | # name: import-shadowing ## (e.g., url) 55 | # name: unhandled-error 56 | # name: line-length-limit 57 | # name: dot-imports 58 | - name: blank-imports 59 | - name: context-as-argument 60 | - name: context-keys-type 61 | - name: early-return 62 | - name: error-naming 63 | - name: error-strings 64 | - name: exported 65 | - name: if-return 66 | - name: increment-decrement 67 | - name: indent-error-flow 68 | - name: modifies-value-receiver 69 | - name: package-comments 70 | - name: range 71 | - name: receiver-naming 72 | - name: redefines-builtin-id 73 | - name: string-of-int 74 | - name: superfluous-else 75 | - name: time-naming 76 | - name: var-naming 77 | - name: var-declaration 78 | - name: unconditional-recursion 79 | - name: unexported-naming 80 | - name: unexported-return 81 | - name: unnecessary-stmt 82 | - name: unreachable-code 83 | - name: unused-parameter 84 | - name: unused-receiver 85 | - name: waitgroup-by-value 86 | gci: 87 | skip-generated: true 88 | govet: 89 | enable-all: true 90 | disable: 91 | - fieldalignment # TODO: enable once in a while (lots of noise though) 92 | - shadow # (e.g., proxyURL) 93 | errcheck: 94 | check-blank: true 95 | check-type-assertions: false 96 | gocritic: 97 | enabled-tags: 98 | - performance 99 | - style 100 | disabled-checks: 101 | - unnamedResult 102 | gosec: 103 | excludes: ## integer overflow; weak rand 104 | - G115 105 | - G402 106 | - G404 107 | prealloc: 108 | simple: true # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them 109 | range-loops: true # Report preallocation suggestions on range loops, true by default 110 | for-loops: true # Report preallocation suggestions on for loops, false by default 111 | misspell: 112 | # Correct spellings using locale preferences for US or UK. 113 | # Default is to use a neutral variety of English. 114 | # Setting locale to US will correct the British spelling of 'colour' to 'color'. 115 | locale: US 116 | depguard: 117 | rules: 118 | main: 119 | deny: 120 | - pkg: io/ioutil 121 | desc: "replaced by io and os packages since Go 1.16: https://tip.golang.org/doc/go1.16#ioutil" 122 | - pkg: 'math/rand$' 123 | desc: "`math/rand` package has been deprecated use `math/rand/v2`" 124 | 125 | issues: 126 | # use default exclude rules 127 | exclude-use-default: true 128 | 129 | # additional exclude rules 130 | exclude-rules: 131 | - linters: [errcheck] 132 | text: "Error return value of" # ignores 'error return value of ... is unchecked' 133 | 134 | - linters: [gocritic] 135 | text: "block doesn't have definitions" # Sometimes blocks are useful for better readability. 136 | 137 | # Set to 0 to disable limit 138 | max-same-issues: 0 139 | max-issues-per-linter: 0 140 | 141 | run: 142 | go: '1.23.5' 143 | tests: true # Enable linting test files. 144 | concurrency: 8 145 | timeout: 8m 146 | -------------------------------------------------------------------------------- /CLA: -------------------------------------------------------------------------------- 1 | Contribution License Agreement 2 | 3 | This Contribution License Agreement (“Agreement”) is agreed to by the party 4 | signing below (“You”), and conveys certain license rights to NVIDIA Corporation 5 | and its affiliates (“NVIDIA”) for Your contributions to NVIDIA open source 6 | projects. This Agreement is effective as of the latest signature date below. 7 | 8 | 1. Definitions. 9 | 10 | “Code” means the computer software code, whether in human-readable or 11 | machine-executable form, that is delivered by You to NVIDIA under this 12 | Agreement. 13 | 14 | “Project” means any of the projects owned or managed by NVIDIA in which 15 | software is offered under a license approved by the Open Source Initiative 16 | (OSI) (www.opensource.org) and documentation offered under an OSI or a 17 | Creative Commons license (https://creativecommons.org/licenses). 18 | 19 | “Submit” is the act of uploading, submitting, transmitting, or distributing 20 | code or other content to any Project, including but not limited to 21 | communication on electronic mailing lists, source code control systems, 22 | and issue tracking systems that are managed by, or on behalf of, the 23 | Project for the purpose of discussing and improving that Project, but 24 | excluding communication that is conspicuously marked or otherwise 25 | designated in writing by You as “Not a Submission.” 26 | 27 | “Submission” means the Code and any other copyrightable material Submitted 28 | by You, including any associated comments and documentation. 29 | 30 | 2. Your Submission. You must agree to the terms of this Agreement before 31 | making a Submission to any Project. This Agreement covers any and all 32 | Submissions that You, now or in the future (except as described in Section 33 | 4 below), Submit to any Project. 34 | 35 | 3. Originality of Work. You represent that each of Your Submissions is 36 | entirely Your original work. Should You wish to Submit materials that are 37 | not Your original work, You may Submit them separately to the Project if 38 | You (a) retain all copyright and license information that was in the 39 | materials as You received them, (b) in the description accompanying Your 40 | Submission, include the phrase “Submission containing materials of a 41 | third party:” followed by the names of the third party and any licenses 42 | or other restrictions of which You are aware, and (c) follow any other 43 | instructions in the Project’s written guidelines concerning Submissions. 44 | 45 | 4. Your Employer. References to “employer” in this Agreement include Your 46 | employer or anyone else for whom You are acting in making Your Submission, 47 | e.g. as a contractor, vendor, or agent. If Your Submission is made in the 48 | course of Your work for an employer or Your employer has intellectual 49 | property rights in Your Submission by contract or applicable law, You must 50 | secure permission from Your employer to make the Submission before signing 51 | this Agreement. In that case, the term “You” in this Agreement will refer 52 | to You and the employer collectively. If You change employers in the 53 | future and desire to Submit additional Submissions for the new employer, 54 | then You agree to sign a new Agreement and secure permission from the 55 | new employer before Submitting those Submissions. 56 | 57 | 58 | 5. Licenses. 59 | 60 | a. Copyright License. You grant NVIDIA, and those who receive the Submission 61 | directly or indirectly from NVIDIA, a perpetual, worldwide, non-exclusive, 62 | royalty-free, irrevocable license in the Submission to reproduce, prepare 63 | derivative works of, publicly display, publicly perform, and distribute the 64 | Submission and such derivative works, and to sublicense any or all of the 65 | foregoing rights to third parties. 66 | 67 | b. Patent License. You grant NVIDIA, and those who receive the Submission 68 | directly or indirectly from NVIDIA, a perpetual, worldwide, non-exclusive, 69 | royalty-free, irrevocable license under Your patent claims that are 70 | necessarily infringed by the Submission or the combination of the Submission 71 | with the Project to which it was Submitted to make, have made, use, offer to 72 | sell, sell and import or otherwise dispose of the Submission alone or with 73 | the Project. 74 | 75 | c. Other Rights Reserved. Each party reserves all rights not expressly 76 | granted in this Agreement. No additional licenses or rights whatsoever 77 | (including, without limitation, any implied licenses) are granted by 78 | implication, exhaustion, estoppel or otherwise. 79 | 80 | 6. Representations and Warranties. You represent that You are legally 81 | entitled to grant the above licenses. You represent that each of Your 82 | Submissions is entirely Your original work (except as You may have 83 | disclosed under Section 3). You represent that You have secured permission 84 | from Your employer to make the Submission in cases where Your Submission 85 | is made in the course of Your work for Your employer or Your employer has 86 | intellectual property rights in Your Submission by contract or applicable 87 | law. If You are signing this Agreement on behalf of Your employer, You 88 | represent and warrant that You have the necessary authority to bind the 89 | listed employer to the obligations contained in this Agreement. You are 90 | not expected to provide support for Your Submission, unless You choose to 91 | do so. UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING, AND 92 | EXCEPT FOR THE WARRANTIES EXPRESSLY STATED IN SECTIONS 3, 4, AND 6, THE 93 | SUBMISSION PROVIDED UNDER THIS AGREEMENT IS PROVIDED WITHOUT WARRANTY OF 94 | ANY KIND, INCLUDING, BUT NOT LIMITED TO, ANY WARRANTY OF NONINFRINGEMENT, 95 | MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. 96 | 97 | 7. Notice to NVIDIA. You agree to notify NVIDIA in writing of any facts 98 | or circumstances of which You later become aware that would make Your 99 | representations in this Agreement inaccurate in any respect. 100 | 101 | 8. Information about Submissions. You agree that contributions to Projects 102 | and information about contributions may be maintained indefinitely and 103 | disclosed publicly, including Your name and other information that You 104 | submit with Your Submission. 105 | 106 | 9. Governing Law/Jurisdiction. Claims arising under this Agreement shall 107 | be governed by the laws of Delaware, excluding its principles of conflict 108 | of laws and the United Nations Convention on Contracts for the Sale of 109 | Goods. The state and/or federal courts residing in Santa Clara County, 110 | California shall have exclusive jurisdiction over any dispute or claim 111 | arising out of this Agreement. You may not export the Software in 112 | violation of applicable export laws and regulations. 113 | 114 | 10. Entire Agreement/Assignment. This Agreement is the entire agreement 115 | between the parties, and supersedes any and all prior agreements, 116 | understandings or communications, written or oral, between the parties 117 | relating to the subject matter hereof. This Agreement may be assigned by 118 | NVIDIA. 119 | 120 | 121 | 122 | 123 | Please select one of the options below and sign as indicated. By signing, 124 | You accept and agree to the terms of this Contribution License Agreement 125 | for Your present and future Submissions to NVIDIA. 126 | 127 | ___ I have sole ownership of intellectual property rights to my Submissions 128 | and I am not making Submissions in the course of work for my employer. 129 | 130 | Name (“You”): _________________________________________ 131 | Signature: _________________________________________ 132 | Date: _________________________________________ 133 | GitHub Login: _________________________________________ 134 | Email: _________________________________________ 135 | Address: _________________________________________ 136 | 137 | ___ I am making Submissions in the course of work for my employer (or my 138 | employer has intellectual property rights in my Submissions by contract or 139 | applicable law). I have permission from my employer to make Submissions and 140 | enter into this Agreement on behalf of my employer. By signing below, the 141 | defined term “You” includes me and my employer. 142 | 143 | Company Name: _________________________________________ 144 | Signature: _________________________________________ 145 | By: _________________________________________ 146 | Title: _________________________________________ 147 | Date: _________________________________________ 148 | GitHub Login: _________________________________________ 149 | Email: _________________________________________ 150 | Address: _________________________________________ 151 | 152 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | # SPDX-License-Identifier: Apache-2.0 3 | 4 | all: fmt build test 5 | 6 | .PHONY: all bench build clean cover fmt lint-update lint test 7 | 8 | bench: 9 | go test -bench . 10 | 11 | build: 12 | go build . 13 | 14 | clean: 15 | go clean -i . 16 | 17 | cover: 18 | go test -cover . 19 | 20 | fmt: 21 | go fmt . 22 | 23 | lint-update: 24 | rm -f $(GOPATH)/bin/golangci-lint 25 | curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(GOPATH)/bin latest 26 | 27 | lint: 28 | golangci-lint run --config .golangci.yml . 29 | 30 | test: 31 | go test . 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sortedmap 2 | 3 | Go package providing sorted maps implemented as either a Left-Leaning Red-Black Tree (in memory) or a B+Tree (pageable) 4 | 5 | ## API Reference 6 | 7 | ``` 8 | type Key interface{} 9 | type Value interface{} 10 | 11 | type Compare func(key1 Key, key2 Key) (result int, err error) 12 | 13 | func CompareInt(key1 Key, key2 Key) (result int, err error) 14 | func CompareUint32(key1 Key, key2 Key) (result int, err error) 15 | func CompareUint64(key1 Key, key2 Key) (result int, err error) 16 | func CompareString(key1 Key, key2 Key) (result int, err error) 17 | func CompareByteSlice(key1 Key, key2 Key) (result int, err error) 18 | func CompareTime(key1 Key, key2 Key) (result int, err error) 19 | 20 | type SortedMap interface { 21 | BisectLeft(key Key) (index int, found bool, err error) // Returns index of matching key:value pair or, if no match, index is to key:value just before where this key would go 22 | BisectRight(key Key) (index int, found bool, err error) // Returns index of matching key:value pair or, if no match, index is to key:value just after where this key would go 23 | DeleteByIndex(index int) (ok bool, err error) 24 | DeleteByKey(key Key) (ok bool, err error) 25 | Dump() (err error) 26 | GetByIndex(index int) (key Key, value Value, ok bool, err error) 27 | GetByKey(key Key) (value Value, ok bool, err error) 28 | Len() (numberOfItems int, err error) 29 | PatchByIndex(index int, value Value) (ok bool, err error) 30 | PatchByKey(key Key, value Value) (ok bool, err error) 31 | Put(key Key, value Value) (ok bool, err error) 32 | Validate() (err error) 33 | } 34 | 35 | type DumpCallbacks interface { 36 | DumpKey(key Key) (keyAsString string, err error) 37 | DumpValue(value Value) (valueAsString string, err error) 38 | } 39 | 40 | type LLRBTree interface { 41 | SortedMap 42 | Reset() 43 | } 44 | 45 | type LLRBTreeCallbacks interface { 46 | DumpCallbacks 47 | } 48 | 49 | func NewLLRBTree(compare Compare, callbacks LLRBTreeCallbacks) (tree LLRBTree) 50 | 51 | var OnDiskByteOrder = cstruct.LittleEndian 52 | 53 | type LayoutReport map[uint64]uint64 54 | 55 | type BPlusTree interface { 56 | SortedMap 57 | FetchLocation() (rootObjectNumber uint64, rootObjectOffset uint64, rootObjectLength uint64) 58 | FetchLayoutReport() (layoutReport LayoutReport, err error) 59 | Flush(andPurge bool) (rootObjectNumber uint64, rootObjectOffset uint64, rootObjectLength uint64, err error) 60 | Purge(full bool) (err error) 61 | Touch() (err error) 62 | TouchItem(thisItemIndexToTouch uint64) (nextItemIndexToTouch uint64, err error) 63 | Prune() (err error) 64 | Discard() (err error) 65 | } 66 | 67 | type BPlusTreeCallbacks interface { 68 | DumpCallbacks 69 | GetNode(objectNumber uint64, objectOffset uint64, objectLength uint64) (nodeByteSlice []byte, err error) 70 | PutNode(nodeByteSlice []byte) (objectNumber uint64, objectOffset uint64, err error) 71 | DiscardNode(objectNumber uint64, objectOffset uint64, objectLength uint64) (err error) 72 | PackKey(key Key) (packedKey []byte, err error) 73 | UnpackKey(payloadData []byte) (key Key, bytesConsumed uint64, err error) 74 | PackValue(value Value) (packedValue []byte, err error) 75 | UnpackValue(payloadData []byte) (value Value, bytesConsumed uint64, err error) 76 | } 77 | 78 | type BPlusTreeCacheStats struct { 79 | EvictLowLimit uint64 80 | EvictHighLimit uint64 81 | CleanLRUItems uint64 82 | DirtyLRUItems uint64 83 | CacheHits uint64 84 | CacheMisses uint64 85 | } 86 | 87 | type BPlusTreeCache interface { 88 | Stats() (bPlusTreeCacheStats *BPlusTreeCacheStats) 89 | UpdateLimits(evictLowLimit uint64, evictHighLimit uint64) 90 | } 91 | 92 | func NewBPlusTreeCache(evictLowLimit uint64, evictHighLimit uint64) (bPlusTreeCache BPlusTreeCache) 93 | 94 | func NewBPlusTree(maxKeysPerNode uint64, compare Compare, callbacks BPlusTreeCallbacks, bPlusTreeCache BPlusTreeCache) (tree BPlusTree) 95 | 96 | func OldBPlusTree(rootObjectNumber uint64, rootObjectOffset uint64, rootObjectLength uint64, compare Compare, callbacks BPlusTreeCallbacks, bPlusTreeCache BPlusTreeCache) (tree BPlusTree, err error) 97 | ``` 98 | 99 | ## Contributors 100 | 101 | * ed@swiftstack.com 102 | 103 | ## License 104 | 105 | See the included LICENSE file 106 | -------------------------------------------------------------------------------- /btree_api.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "fmt" 8 | 9 | "github.com/NVIDIA/cstruct" 10 | ) 11 | 12 | // OnDiskByteOrder specifies the endian-ness expected to be used to persist B+Tree data structures 13 | var OnDiskByteOrder = cstruct.LittleEndian 14 | 15 | // LayoutReport is a map where key is an objectNumber and value is objectBytes used in that objectNumber 16 | type LayoutReport map[uint64]uint64 17 | 18 | type DimensionsReport struct { 19 | MinKeysPerNode uint64 // only applies to non-Root nodes 20 | MaxKeysPerNode uint64 21 | Items uint64 22 | Height uint64 23 | } 24 | 25 | // BPlusTree interface declares the available methods available for a B+Tree 26 | type BPlusTree interface { 27 | SortedMap 28 | FetchLocation() (rootObjectNumber uint64, rootObjectOffset uint64, rootObjectLength uint64) 29 | FetchLayoutReport() (layoutReport LayoutReport, err error) 30 | FetchDimensionsReport() (dimensionsReport DimensionsReport, err error) 31 | Flush(andPurge bool) (rootObjectNumber uint64, rootObjectOffset uint64, rootObjectLength uint64, err error) 32 | Purge(full bool) (err error) 33 | Touch() (err error) 34 | TouchItem(thisItemIndexToTouch uint64) (nextItemIndexToTouch uint64, err error) 35 | Prune() (err error) 36 | Discard() (err error) 37 | } 38 | 39 | // BPlusTreeCallbacks specifies the interface to a set of callbacks provided by the client 40 | type BPlusTreeCallbacks interface { 41 | DumpCallbacks 42 | GetNode(objectNumber uint64, objectOffset uint64, objectLength uint64) (nodeByteSlice []byte, err error) 43 | PutNode(nodeByteSlice []byte) (objectNumber uint64, objectOffset uint64, err error) 44 | DiscardNode(objectNumber uint64, objectOffset uint64, objectLength uint64) (err error) 45 | PackKey(key Key) (packedKey []byte, err error) 46 | UnpackKey(payloadData []byte) (key Key, bytesConsumed uint64, err error) 47 | PackValue(value Value) (packedValue []byte, err error) 48 | UnpackValue(payloadData []byte) (value Value, bytesConsumed uint64, err error) 49 | } 50 | 51 | type BPlusTreeCacheStats struct { 52 | EvictLowLimit uint64 53 | EvictHighLimit uint64 54 | CleanLRUItems uint64 55 | DirtyLRUItems uint64 56 | CacheHits uint64 57 | CacheMisses uint64 58 | } 59 | 60 | type BPlusTreeCache interface { 61 | Stats() (bPlusTreeCacheStats *BPlusTreeCacheStats) 62 | UpdateLimits(evictLowLimit uint64, evictHighLimit uint64) 63 | } 64 | 65 | func NewBPlusTreeCache(evictLowLimit, evictHighLimit uint64) (bPlusTreeCache BPlusTreeCache) { 66 | bPlusTreeCache = &btreeNodeCacheStruct{ 67 | evictLowLimit: evictLowLimit, 68 | evictHighLimit: evictHighLimit, 69 | cleanLRUHead: nil, 70 | cleanLRUTail: nil, 71 | cleanLRUItems: 0, 72 | dirtyLRUHead: nil, 73 | dirtyLRUTail: nil, 74 | dirtyLRUItems: 0, 75 | drainerActive: false, 76 | cacheHits: 0, 77 | cacheMisses: 0, 78 | } 79 | return 80 | } 81 | 82 | // NewBPlusTree is used to construct an in-memory B+Tree supporting the Tree interface 83 | // 84 | // The supplied value of maxKeysPerNode, being precisely twice the computed value of 85 | // (uint64) minKeysPerNode, must be even. In addition, minKeysPerNode must be computed 86 | // to be greater than one to ensure that during node merge operations, there is always 87 | // at least one sibling node with which to merge. Hence, maxKeysPerNode must also be 88 | // at least four. 89 | // 90 | // Note that if the B+Tree will reside only in memory, callback argument may be nil. 91 | // That said, there is no advantage to using a B+Tree for an in-memory collection over 92 | // the llrb-provided collection implementing the same APIs. 93 | func NewBPlusTree(maxKeysPerNode uint64, compare Compare, callbacks BPlusTreeCallbacks, bPlusTreeCache BPlusTreeCache) (tree BPlusTree) { 94 | minKeysPerNode := maxKeysPerNode >> 1 95 | if (4 > maxKeysPerNode) || ((2 * minKeysPerNode) != maxKeysPerNode) { 96 | err := fmt.Errorf("maxKeysPerNode (%v) invalid - must be an even positive number greater than 3", maxKeysPerNode) 97 | panic(err) 98 | } 99 | 100 | rootNode := &btreeNodeStruct{ 101 | objectNumber: 0, // To be filled in once root node is posted 102 | objectOffset: 0, // To be filled in once root node is posted 103 | objectLength: 0, // To be filled in once root node is posted 104 | items: 0, 105 | loaded: true, // Special case in that objectNumber == 0 means it has no onDisk copy 106 | dirty: true, // To be set just below 107 | root: true, 108 | leaf: true, 109 | tree: nil, // To be set just below 110 | parentNode: nil, 111 | kvLLRB: NewLLRBTree(compare, callbacks), 112 | nonLeafLeftChild: nil, 113 | rootPrefixSumChild: nil, 114 | prefixSumItems: 0, // Not applicable to root node 115 | prefixSumParent: nil, // Not applicable to root node 116 | prefixSumLeftChild: nil, // Not applicable to root node 117 | prefixSumRightChild: nil, // Not applicable to root node 118 | } 119 | 120 | rootNode.btreeNodeCacheElement.btreeNodeCacheTag = noLRU 121 | 122 | treePtr := &btreeTreeStruct{ 123 | minKeysPerNode: minKeysPerNode, 124 | maxKeysPerNode: maxKeysPerNode, 125 | Compare: compare, 126 | BPlusTreeCallbacks: callbacks, 127 | root: rootNode, 128 | staleOnDiskReferencesList: nil, 129 | } 130 | 131 | if bPlusTreeCache == nil { 132 | treePtr.nodeCache = nil 133 | } else { 134 | treePtr.nodeCache = bPlusTreeCache.(*btreeNodeCacheStruct) 135 | } 136 | 137 | rootNode.tree = treePtr 138 | 139 | treePtr.initNodeAsEvicted(rootNode) 140 | treePtr.markNodeDirty(rootNode) 141 | 142 | tree = treePtr 143 | 144 | return 145 | } 146 | 147 | // OldBPlusTree is used to re-construct a B+Tree previously persisted 148 | func OldBPlusTree(rootObjectNumber, rootObjectOffset, rootObjectLength uint64, compare Compare, callbacks BPlusTreeCallbacks, bPlusTreeCache BPlusTreeCache) (tree BPlusTree, err error) { 149 | rootNode := &btreeNodeStruct{ 150 | objectNumber: rootObjectNumber, 151 | objectOffset: rootObjectOffset, 152 | objectLength: rootObjectLength, 153 | items: 0, // To be filled in once root node is loaded 154 | loaded: false, 155 | dirty: false, 156 | root: true, 157 | leaf: true, // To be updated once root node is loaded 158 | tree: nil, // To be set just below 159 | parentNode: nil, 160 | kvLLRB: nil, // To be filled in once root node is loaded 161 | nonLeafLeftChild: nil, // To be filled in once root node is loaded 162 | rootPrefixSumChild: nil, // To be filled in once root node is loaded (nil if root is also leaf) 163 | prefixSumItems: 0, // Not applicable to root node 164 | prefixSumParent: nil, // Not applicable to root node 165 | prefixSumLeftChild: nil, // Not applicable to root node 166 | prefixSumRightChild: nil, // Not applicable to root node 167 | } 168 | 169 | rootNode.btreeNodeCacheElement.btreeNodeCacheTag = noLRU 170 | 171 | treePtr := &btreeTreeStruct{ 172 | minKeysPerNode: 0, // To be filled in once root node is loaded 173 | maxKeysPerNode: 0, // To be filled in once root node is loaded 174 | Compare: compare, 175 | BPlusTreeCallbacks: callbacks, 176 | root: rootNode, 177 | staleOnDiskReferencesList: nil, 178 | } 179 | 180 | if bPlusTreeCache == nil { 181 | treePtr.nodeCache = nil 182 | } else { 183 | treePtr.nodeCache = bPlusTreeCache.(*btreeNodeCacheStruct) 184 | } 185 | 186 | rootNode.tree = treePtr 187 | 188 | treePtr.initNodeAsEvicted(rootNode) 189 | 190 | tree = treePtr 191 | 192 | err = treePtr.loadNode(rootNode) // Return from loadNode() sufficient for return from this func 193 | 194 | return 195 | } 196 | -------------------------------------------------------------------------------- /btree_balance_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | "reflect" 10 | "strconv" 11 | "testing" 12 | ) 13 | 14 | type balanceBPlusTreeTestContextStruct struct{} 15 | 16 | func TestBPlusTreeBalance(t *testing.T) { 17 | const ( 18 | maxKeysPerNode = 32 19 | numKeys = int(10000) 20 | ) 21 | var ( 22 | err error 23 | key int 24 | keyIndex int 25 | keysToDelete []int 26 | keysToPut []int 27 | ok bool 28 | tree BPlusTree // map[int]struct{} 29 | treeContext *balanceBPlusTreeTestContextStruct 30 | treeLen int 31 | treeLenExpected int 32 | ) 33 | 34 | treeContext = &balanceBPlusTreeTestContextStruct{} 35 | 36 | tree = NewBPlusTree(maxKeysPerNode, CompareInt, treeContext, nil) 37 | 38 | keysToPut, err = testKnuthShuffledIntSlice(numKeys) 39 | if err != nil { 40 | t.Fatalf("testKnuthShuffledIntSlice() [Case A] failed: %v", err) 41 | } 42 | 43 | for keyIndex, key = range keysToPut { 44 | ok, err = tree.Put(key, struct{}{}) 45 | if err != nil { 46 | t.Fatalf("tree.Put(%d,) [Case A] failed: %v", key, err) 47 | } 48 | if !ok { 49 | t.Fatalf("tree.Put(%d,) [Case A] returned !ok", key) 50 | } 51 | treeLen, err = tree.Len() 52 | if err != nil { 53 | t.Fatalf("tree.Len() [Case A] failed: %v", err) 54 | } 55 | treeLenExpected = keyIndex + 1 56 | if treeLen != treeLenExpected { 57 | t.Fatalf("tree.Len() [Case A] returned %d...expected %d", treeLen, treeLenExpected) 58 | } 59 | err = tree.Validate() 60 | if err != nil { 61 | t.Fatalf("tree.Validate() [Case A] failed: %v", err) 62 | } 63 | } 64 | 65 | keysToDelete, err = testKnuthShuffledIntSlice(numKeys) 66 | if err != nil { 67 | t.Fatalf("testKnuthShuffledIntSlice() [Case B] failed: %v", err) 68 | } 69 | 70 | for keyIndex, key = range keysToDelete { 71 | ok, err = tree.DeleteByKey(key) 72 | if err != nil { 73 | t.Fatalf("tree.DeleteByKey(%d) failed: %v", key, err) 74 | } 75 | if !ok { 76 | t.Fatalf("tree.DeleteByKey(%d) returned !ok", key) 77 | } 78 | treeLen, err = tree.Len() 79 | if err != nil { 80 | t.Fatalf("tree.Len() [Case B] failed: %v", err) 81 | } 82 | treeLenExpected = numKeys - keyIndex - 1 83 | if treeLen != treeLenExpected { 84 | t.Fatalf("tree.Len() [Case B] returned %d...expected %d", treeLen, treeLenExpected) 85 | } 86 | err = tree.Validate() 87 | if err != nil { 88 | t.Fatalf("tree.Validate() [Case B] failed: %v", err) 89 | } 90 | } 91 | 92 | keysToPut, err = testKnuthShuffledIntSlice(numKeys) 93 | if err != nil { 94 | t.Fatalf("testKnuthShuffledIntSlice() [Case C] failed: %v", err) 95 | } 96 | 97 | for keyIndex, key = range keysToPut { 98 | ok, err = tree.Put(key, struct{}{}) 99 | if err != nil { 100 | t.Fatalf("tree.Put(%d,) [Case B] failed: %v", key, err) 101 | } 102 | if !ok { 103 | t.Fatalf("tree.Put(%d,) [Case B] returned !ok", key) 104 | } 105 | treeLen, err = tree.Len() 106 | if err != nil { 107 | t.Fatalf("tree.Len() [Case C] failed: %v", err) 108 | } 109 | treeLenExpected = keyIndex + 1 110 | if treeLen != treeLenExpected { 111 | t.Fatalf("tree.Len() [Case C] returned %d...expected %d", treeLen, treeLenExpected) 112 | } 113 | err = tree.Validate() 114 | if err != nil { 115 | t.Fatalf("tree.Validate() [Case C] failed: %v", err) 116 | } 117 | } 118 | 119 | for keyIndex = (numKeys - 1); keyIndex >= 0; keyIndex-- { 120 | ok, err = tree.DeleteByIndex(keyIndex) 121 | if err != nil { 122 | t.Fatalf("tree.DeleteByIndex(%d) failed: %v", key, err) 123 | } 124 | if !ok { 125 | t.Fatalf("tree.DeleteByIndex(%d) returned !ok", key) 126 | } 127 | treeLen, err = tree.Len() 128 | if err != nil { 129 | t.Fatalf("tree.Len() [Case D] failed: %v", err) 130 | } 131 | treeLenExpected = keyIndex 132 | if treeLen != treeLenExpected { 133 | t.Fatalf("tree.Len() [Case D] returned %d...expected %d", treeLen, treeLenExpected) 134 | } 135 | err = tree.Validate() 136 | if err != nil { 137 | t.Fatalf("tree.Validate() [Case D] failed: %v", err) 138 | } 139 | } 140 | } 141 | 142 | func (*balanceBPlusTreeTestContextStruct) DumpKey(key Key) (keyAsString string, err error) { 143 | var ( 144 | keyAsInt int 145 | ok bool 146 | ) 147 | 148 | keyAsInt, ok = key.(int) 149 | if !ok { 150 | err = fmt.Errorf("DumpKey() expected key of type int... instead it was of type %v", reflect.TypeOf(key)) 151 | return 152 | } 153 | 154 | keyAsString = strconv.Itoa(keyAsInt) 155 | 156 | err = nil 157 | return 158 | } 159 | 160 | func (*balanceBPlusTreeTestContextStruct) DumpValue(_ Value) (valueAsString string, err error) { 161 | valueAsString = "" 162 | err = nil 163 | return 164 | } 165 | 166 | func (*balanceBPlusTreeTestContextStruct) GetNode(_, _, _ uint64) (nodeByteSlice []byte, err error) { 167 | err = errors.New("GetNode() not supported") 168 | return 169 | } 170 | 171 | func (*balanceBPlusTreeTestContextStruct) PutNode(_ []byte) (objectNumber, objectOffset uint64, err error) { 172 | err = errors.New("PutNode() not supported") 173 | return 174 | } 175 | 176 | func (*balanceBPlusTreeTestContextStruct) DiscardNode(_, _, _ uint64) (err error) { 177 | err = errors.New("DiscardNode() not supported") 178 | return 179 | } 180 | 181 | func (*balanceBPlusTreeTestContextStruct) PackKey(_ Key) (packedKey []byte, err error) { 182 | err = errors.New("PackKey() not supported") 183 | return 184 | } 185 | 186 | func (*balanceBPlusTreeTestContextStruct) UnpackKey(_ []byte) (key Key, bytesConsumed uint64, err error) { 187 | err = errors.New("UnpackKey() not supported") 188 | return 189 | } 190 | 191 | func (*balanceBPlusTreeTestContextStruct) PackValue(_ Value) (packedValue []byte, err error) { 192 | err = errors.New("PackValue() not supported") 193 | return 194 | } 195 | 196 | func (*balanceBPlusTreeTestContextStruct) UnpackValue(_ []byte) (value Value, bytesConsumed uint64, err error) { 197 | err = errors.New("UnpackValue() not supported") 198 | return 199 | } 200 | -------------------------------------------------------------------------------- /btree_cache_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "fmt" 8 | "reflect" 9 | "sync" 10 | "testing" 11 | "time" 12 | ) 13 | 14 | const ( 15 | testBPlusTreeCacheDelay = 100 * time.Millisecond 16 | ) 17 | 18 | type cacheBPlusTreeTestContextStruct struct { 19 | sync.Mutex 20 | nextObjectNumber uint64 21 | objectMap map[uint64][]byte 22 | } 23 | 24 | func (*cacheBPlusTreeTestContextStruct) DumpKey(key Key) (keyAsString string, err error) { 25 | var ( 26 | keyAsUint16 uint16 27 | ok bool 28 | ) 29 | 30 | keyAsUint16, ok = key.(uint16) 31 | if !ok { 32 | err = fmt.Errorf("DumpKey() expected key of type uint16... instead it was of type %v", reflect.TypeOf(key)) 33 | return 34 | } 35 | 36 | keyAsString = fmt.Sprintf("0x%04X", keyAsUint16) 37 | 38 | err = nil 39 | return 40 | } 41 | 42 | func (*cacheBPlusTreeTestContextStruct) DumpValue(value Value) (valueAsString string, err error) { 43 | var ( 44 | ok bool 45 | valueAsUint32 uint32 46 | ) 47 | 48 | valueAsUint32, ok = value.(uint32) 49 | if !ok { 50 | err = fmt.Errorf("DumpValue() expected value of type uint32... instead it was of type %v", reflect.TypeOf(value)) 51 | return 52 | } 53 | 54 | valueAsString = fmt.Sprintf("0x%08X", valueAsUint32) 55 | 56 | err = nil 57 | return 58 | } 59 | 60 | func (tree *cacheBPlusTreeTestContextStruct) GetNode(objectNumber, objectOffset, objectLength uint64) (nodeByteSlice []byte, err error) { 61 | var ( 62 | ok bool 63 | ) 64 | 65 | tree.Lock() 66 | defer tree.Unlock() 67 | 68 | nodeByteSlice, ok = tree.objectMap[objectNumber] 69 | if !ok { 70 | err = fmt.Errorf("GetNode() called for non-existent objectNumber 0x%016X", objectNumber) 71 | return 72 | } 73 | if uint64(0) != objectOffset { 74 | err = fmt.Errorf("GetNode() called for non-zero objectOffset 0x%016X", objectOffset) 75 | return 76 | } 77 | if uint64(len(nodeByteSlice)) != objectLength { 78 | err = fmt.Errorf("GetNode() called for objectLength 0x%016X... for node of length 0x%016X", objectLength, uint64(len(nodeByteSlice))) 79 | return 80 | } 81 | 82 | err = nil 83 | return 84 | } 85 | 86 | func (tree *cacheBPlusTreeTestContextStruct) PutNode(nodeByteSlice []byte) (objectNumber, objectOffset uint64, err error) { 87 | tree.Lock() 88 | defer tree.Unlock() 89 | 90 | objectNumber = tree.nextObjectNumber 91 | tree.nextObjectNumber++ 92 | tree.objectMap[objectNumber] = nodeByteSlice 93 | 94 | objectOffset = 0 95 | 96 | err = nil 97 | return 98 | } 99 | 100 | func (tree *cacheBPlusTreeTestContextStruct) DiscardNode(objectNumber, objectOffset, objectLength uint64) (err error) { 101 | var ( 102 | ok bool 103 | nodeByteSlice []byte 104 | ) 105 | 106 | tree.Lock() 107 | defer tree.Unlock() 108 | 109 | nodeByteSlice, ok = tree.objectMap[objectNumber] 110 | if !ok { 111 | err = fmt.Errorf("DiscardNode() called for non-existent objectNumber 0x%016X", objectNumber) 112 | return 113 | } 114 | if uint64(0) != objectOffset { 115 | err = fmt.Errorf("DiscardNode() called for non-zero objectOffset 0x%016X", objectOffset) 116 | return 117 | } 118 | if uint64(len(nodeByteSlice)) != objectLength { 119 | err = fmt.Errorf("DiscardNode() called for objectLength 0x%016X... for node of length 0x%016X", objectLength, uint64(len(nodeByteSlice))) 120 | return 121 | } 122 | 123 | err = nil 124 | return 125 | } 126 | 127 | func (*cacheBPlusTreeTestContextStruct) PackKey(key Key) (packedKey []byte, err error) { 128 | var ( 129 | ok bool 130 | keyAsUint16 uint16 131 | ) 132 | 133 | keyAsUint16, ok = key.(uint16) 134 | if !ok { 135 | err = fmt.Errorf("PackKey() expected key of type uint16... instead it was of type %v", reflect.TypeOf(key)) 136 | return 137 | } 138 | 139 | packedKey = make([]byte, 2) 140 | packedKey[0] = uint8(keyAsUint16 & uint16(0x00FF) >> 0) 141 | packedKey[1] = uint8(keyAsUint16 & uint16(0xFF00) >> 8) 142 | 143 | err = nil 144 | return 145 | } 146 | 147 | func (*cacheBPlusTreeTestContextStruct) UnpackKey(payloadData []byte) (key Key, bytesConsumed uint64, err error) { 148 | if len(payloadData) < 2 { 149 | err = fmt.Errorf("UnpackKey() called for length %v... expected length of at least 2", len(payloadData)) 150 | return 151 | } 152 | 153 | key = uint16(payloadData[0]) | (uint16(payloadData[1]) << 8) 154 | 155 | bytesConsumed = 2 156 | 157 | err = nil 158 | return 159 | } 160 | 161 | func (*cacheBPlusTreeTestContextStruct) PackValue(value Value) (packedValue []byte, err error) { 162 | var ( 163 | ok bool 164 | valueAsUint32 uint32 165 | ) 166 | 167 | valueAsUint32, ok = value.(uint32) 168 | if !ok { 169 | err = fmt.Errorf("PackValue() expected value of type uint32... instead it was of type %v", reflect.TypeOf(value)) 170 | return 171 | } 172 | 173 | packedValue = make([]byte, 4) 174 | packedValue[0] = uint8(valueAsUint32 & uint32(0x000000FF) >> 0) 175 | packedValue[1] = uint8(valueAsUint32 & uint32(0x0000FF00) >> 8) 176 | packedValue[2] = uint8(valueAsUint32 & uint32(0x00FF0000) >> 16) 177 | packedValue[3] = uint8(valueAsUint32 & uint32(0xFF000000) >> 24) 178 | 179 | err = nil 180 | return 181 | } 182 | 183 | func (*cacheBPlusTreeTestContextStruct) UnpackValue(payloadData []byte) (value Value, bytesConsumed uint64, err error) { 184 | if len(payloadData) < 4 { 185 | err = fmt.Errorf("UnpackValue() called for length %v... expected length of at least 4", len(payloadData)) 186 | return 187 | } 188 | 189 | value = uint32(payloadData[0]) | (uint32(payloadData[1]) << 8) | (uint32(payloadData[2]) << 16) | (uint32(payloadData[3]) << 24) 190 | 191 | bytesConsumed = 4 192 | 193 | err = nil 194 | return 195 | } 196 | 197 | func TestBPlusTreeCache(t *testing.T) { 198 | var ( 199 | treeA BPlusTree // map[uint16]uint32 200 | treeB BPlusTree // map[uint16]uint32 201 | treeCache BPlusTreeCache 202 | treeCacheStruct *btreeNodeCacheStruct 203 | treeCacheStats *BPlusTreeCacheStats 204 | treeContext *cacheBPlusTreeTestContextStruct 205 | ) 206 | 207 | treeContext = &cacheBPlusTreeTestContextStruct{ 208 | nextObjectNumber: uint64(0), 209 | objectMap: make(map[uint64][]byte), 210 | } 211 | 212 | treeCache = NewBPlusTreeCache(1, 4) 213 | treeCacheStruct = treeCache.(*btreeNodeCacheStruct) 214 | 215 | // Consume 1 node for treeA 216 | 217 | treeA = NewBPlusTree(4, CompareUint16, treeContext, treeCache) 218 | 219 | _, _ = treeA.Put(uint16(0x0000), uint32(0x00000000)) 220 | 221 | if treeCacheStruct.cleanLRUItems != 0 { 222 | t.Fatalf("Expected treeCacheStruct.cleanLRUItems to be 0 (was %v)", treeCacheStruct.cleanLRUItems) 223 | } 224 | if treeCacheStruct.dirtyLRUItems != 1 { 225 | t.Fatalf("Expected treeCacheStruct.dirtyLRUItems to be 1 (was %v)", treeCacheStruct.dirtyLRUItems) 226 | } 227 | 228 | treeA.Flush(false) 229 | 230 | if treeCacheStruct.cleanLRUItems != 1 { 231 | t.Fatalf("Expected treeCacheStruct.cleanLRUItems to be 1 (was %v)", treeCacheStruct.cleanLRUItems) 232 | } 233 | if treeCacheStruct.dirtyLRUItems != 0 { 234 | t.Fatalf("Expected treeCacheStruct.dirtyLRUItems to be 0 (was %v)", treeCacheStruct.dirtyLRUItems) 235 | } 236 | 237 | treeA.Flush(true) 238 | 239 | if treeCacheStruct.cleanLRUItems != 0 { 240 | t.Fatalf("Expected treeCacheStruct.cleanLRUItems to be 0 (was %v)", treeCacheStruct.cleanLRUItems) 241 | } 242 | if treeCacheStruct.dirtyLRUItems != 0 { 243 | t.Fatalf("Expected treeCacheStruct.dirtyLRUItems to be 0 (was %v)", treeCacheStruct.dirtyLRUItems) 244 | } 245 | 246 | // Consume 4 nodes for treeB 247 | 248 | treeB = NewBPlusTree(4, CompareUint16, treeContext, treeCache) 249 | 250 | _, _ = treeB.Put(uint16(0x0000), uint32(0x00000000)) 251 | _, _ = treeB.Put(uint16(0x0001), uint32(0x00000001)) 252 | _, _ = treeB.Put(uint16(0x0002), uint32(0x00000002)) 253 | _, _ = treeB.Put(uint16(0x0003), uint32(0x00000003)) 254 | _, _ = treeB.Put(uint16(0x0004), uint32(0x00000004)) 255 | _, _ = treeB.Put(uint16(0x0005), uint32(0x00000005)) 256 | _, _ = treeB.Put(uint16(0x0006), uint32(0x00000006)) 257 | 258 | if treeCacheStruct.cleanLRUItems != 0 { 259 | t.Fatalf("Expected treeCacheStruct.cleanLRUItems to be 0 (was %v)", treeCacheStruct.cleanLRUItems) 260 | } 261 | if treeCacheStruct.dirtyLRUItems != 4 { 262 | t.Fatalf("Expected treeCacheStruct.dirtyLRUItems to be 4 (was %v)", treeCacheStruct.dirtyLRUItems) 263 | } 264 | 265 | treeB.Flush(false) 266 | 267 | if treeCacheStruct.cleanLRUItems != 4 { 268 | t.Fatalf("Expected treeCacheStruct.cleanLRUItems to be 4 (was %v)", treeCacheStruct.cleanLRUItems) 269 | } 270 | if treeCacheStruct.dirtyLRUItems != 0 { 271 | t.Fatalf("Expected treeCacheStruct.dirtyLRUItems to be 0 (was %v)", treeCacheStruct.dirtyLRUItems) 272 | } 273 | 274 | treeB.Flush(true) 275 | 276 | if treeCacheStruct.cleanLRUItems != 0 { 277 | t.Fatalf("Expected treeCacheStruct.cleanLRUItems to be 0 (was %v)", treeCacheStruct.cleanLRUItems) 278 | } 279 | if treeCacheStruct.dirtyLRUItems != 0 { 280 | t.Fatalf("Expected treeCacheStruct.dirtyLRUItems to be 0 (was %v)", treeCacheStruct.dirtyLRUItems) 281 | } 282 | 283 | // Read entire treeB filling all 4 treeCacheStruct.cleanLRUItems 284 | 285 | _, _, _ = treeB.GetByKey(uint16(0x0000)) 286 | _, _, _ = treeB.GetByKey(uint16(0x0001)) 287 | _, _, _ = treeB.GetByKey(uint16(0x0002)) 288 | _, _, _ = treeB.GetByKey(uint16(0x0003)) 289 | _, _, _ = treeB.GetByKey(uint16(0x0004)) 290 | _, _, _ = treeB.GetByKey(uint16(0x0005)) 291 | _, _, _ = treeB.GetByKey(uint16(0x0006)) 292 | 293 | if treeCacheStruct.cleanLRUItems != 4 { 294 | t.Fatalf("Expected treeCacheStruct.cleanLRUItems to be 4 (was %v)", treeCacheStruct.cleanLRUItems) 295 | } 296 | if treeCacheStruct.dirtyLRUItems != 0 { 297 | t.Fatalf("Expected treeCacheStruct.dirtyLRUItems to be 0 (was %v)", treeCacheStruct.dirtyLRUItems) 298 | } 299 | 300 | // Read lone key from treeA expecting treeCache to have to purge down to evictLowLimit 301 | 302 | _, _, _ = treeA.GetByKey(uint16(0x0000)) 303 | 304 | for treeCacheStruct.drainerActive { 305 | time.Sleep(testBPlusTreeCacheDelay) 306 | } 307 | 308 | if treeCacheStruct.cleanLRUItems != 1 { 309 | t.Fatalf("Expected treeCacheStruct.cleanLRUItems to be 1 (was %v)", treeCacheStruct.cleanLRUItems) 310 | } 311 | if treeCacheStruct.dirtyLRUItems != 0 { 312 | t.Fatalf("Expected treeCacheStruct.dirtyLRUItems to be 0 (was %v)", treeCacheStruct.dirtyLRUItems) 313 | } 314 | 315 | // Finally, query treeCacheStruct Stats() 316 | 317 | treeCacheStats = treeCacheStruct.Stats() 318 | 319 | if treeCacheStats.EvictLowLimit != 1 { 320 | t.Fatalf("Expected EvictLowLimit to be 1 (was %v)", treeCacheStats.EvictLowLimit) 321 | } 322 | if treeCacheStats.EvictHighLimit != 4 { 323 | t.Fatalf("Expected EvictHighLimit to be 4 (was %v)", treeCacheStats.EvictHighLimit) 324 | } 325 | if treeCacheStats.CleanLRUItems != 1 { 326 | t.Fatalf("Expected CleanLRUItems to be 1 (was %v)", treeCacheStats.CleanLRUItems) 327 | } 328 | if treeCacheStats.DirtyLRUItems != 0 { 329 | t.Fatalf("Expected DirtyLRUItems to be 0 (was %v)", treeCacheStats.DirtyLRUItems) 330 | } 331 | if treeCacheStats.CacheHits != 20 { 332 | t.Fatalf("Expected CacheHits to be 20 (was %v)", treeCacheStats.CacheHits) 333 | } 334 | if treeCacheStats.CacheMisses != 5 { 335 | t.Fatalf("Expected CacheMisses to be 5 (was %v)", treeCacheStats.CacheMisses) 336 | } 337 | } 338 | -------------------------------------------------------------------------------- /btree_common_api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "encoding/binary" 8 | "errors" 9 | "strconv" 10 | "testing" 11 | ) 12 | 13 | const ( 14 | commonBPlusTreeTestNumKeysMaxSmall = uint64(4) 15 | commonBPlusTreeTestNumKeysMaxModest = uint64(10) 16 | commonBPlusTreeTestNumKeysMaxTypical = uint64(100) 17 | commonBPlusTreeTestNumKeysMaxLarge = uint64(1000) 18 | 19 | commonBPlusTreeBenchmarkNumKeys = 10000 20 | ) 21 | 22 | type commonBPlusTreeTestContextStruct struct { 23 | t *testing.T 24 | tree BPlusTree 25 | } 26 | 27 | func (*commonBPlusTreeTestContextStruct) GetNode(_, _, _ uint64) (nodeByteSlice []byte, err error) { 28 | err = errors.New("GetNode() not implemented") 29 | return 30 | } 31 | 32 | func (*commonBPlusTreeTestContextStruct) PutNode(_ []byte) (objectNumber, objectOffset uint64, err error) { 33 | err = errors.New("PutNode() not implemented") 34 | return 35 | } 36 | 37 | func (*commonBPlusTreeTestContextStruct) DiscardNode(_, _, _ uint64) (err error) { 38 | err = errors.New("DiscardNode() not implemented") 39 | return 40 | } 41 | 42 | func (context *commonBPlusTreeTestContextStruct) DumpKey(key Key) (keyAsString string, err error) { 43 | keyAsInt, ok := key.(int) 44 | if !ok { 45 | context.t.Fatalf("DumpKey() argument not an int") 46 | } 47 | keyAsString = strconv.Itoa(keyAsInt) 48 | err = nil 49 | return 50 | } 51 | 52 | func (context *commonBPlusTreeTestContextStruct) PackKey(key Key) (packedKey []byte, err error) { 53 | keyAsInt, ok := key.(int) 54 | if !ok { 55 | context.t.Fatalf("PackKey() argument not an int") 56 | } 57 | keyAsUint64 := uint64(keyAsInt) 58 | packedKey = make([]byte, 8) 59 | binary.LittleEndian.PutUint64(packedKey, keyAsUint64) 60 | err = nil 61 | return 62 | } 63 | 64 | func (context *commonBPlusTreeTestContextStruct) UnpackKey(_ []byte) (key Key, bytesConsumed uint64, err error) { 65 | context.t.Fatalf("UnpackKey() not implemented") 66 | return 67 | } 68 | 69 | func (context *commonBPlusTreeTestContextStruct) DumpValue(value Value) (valueAsString string, err error) { 70 | valueAsString, ok := value.(string) 71 | if !ok { 72 | context.t.Fatalf("PackValue() argument not a string") 73 | } 74 | err = nil 75 | return 76 | } 77 | 78 | func (context *commonBPlusTreeTestContextStruct) PackValue(value Value) (packedValue []byte, err error) { 79 | valueAsString, ok := value.(string) 80 | if !ok { 81 | context.t.Fatalf("PackValue() argument not a string") 82 | } 83 | packedValue = []byte(valueAsString) 84 | err = nil 85 | return 86 | } 87 | 88 | func (context *commonBPlusTreeTestContextStruct) UnpackValue(_ []byte) (value Value, bytesConsumed uint64, err error) { 89 | context.t.Fatalf("UnpackValue() not implemented") 90 | return 91 | } 92 | 93 | type commonBPlusTreeBenchmarkContextStruct struct { 94 | b *testing.B 95 | tree BPlusTree 96 | } 97 | 98 | func (*commonBPlusTreeBenchmarkContextStruct) GetNode(_, _, _ uint64) (nodeByteSlice []byte, err error) { 99 | err = errors.New("GetNode() not implemented") 100 | return 101 | } 102 | 103 | func (*commonBPlusTreeBenchmarkContextStruct) PutNode(_ []byte) (objectNumber, objectOffset uint64, err error) { 104 | err = errors.New("PutNode() not implemented") 105 | return 106 | } 107 | 108 | func (*commonBPlusTreeBenchmarkContextStruct) DiscardNode(_, _, _ uint64) (err error) { 109 | err = errors.New("GetNode() not implemented") 110 | return 111 | } 112 | 113 | func (*commonBPlusTreeBenchmarkContextStruct) DumpKey(_ Key) (keyAsString string, err error) { 114 | err = errors.New("DumpKey() not implemented") 115 | return 116 | } 117 | 118 | func (*commonBPlusTreeBenchmarkContextStruct) PackKey(_ Key) (packedKey []byte, err error) { 119 | err = errors.New("PackKey() not implemented") 120 | return 121 | } 122 | 123 | func (*commonBPlusTreeBenchmarkContextStruct) UnpackKey(_ []byte) (key Key, bytesConsumed uint64, err error) { 124 | err = errors.New("UnpackKey() not implemented") 125 | return 126 | } 127 | 128 | func (*commonBPlusTreeBenchmarkContextStruct) DumpValue(_ Value) (valueAsString string, err error) { 129 | err = errors.New("DumpValue() not implemented") 130 | return 131 | } 132 | 133 | func (*commonBPlusTreeBenchmarkContextStruct) PackValue(_ Value) (packedValue []byte, err error) { 134 | err = errors.New("PackValue() not implemented") 135 | return 136 | } 137 | 138 | func (*commonBPlusTreeBenchmarkContextStruct) UnpackValue(_ []byte) (value Value, bytesConsumed uint64, err error) { 139 | err = errors.New("UnpackValue() not implemented") 140 | return 141 | } 142 | 143 | func TestBPlusTreeAllButDeleteSimple(t *testing.T) { 144 | context := &commonBPlusTreeTestContextStruct{t: t} 145 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 146 | metaTestAllButDeleteSimple(t, context.tree) 147 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 148 | metaTestAllButDeleteSimple(t, context.tree) 149 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 150 | metaTestAllButDeleteSimple(t, context.tree) 151 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 152 | metaTestAllButDeleteSimple(t, context.tree) 153 | } 154 | 155 | func TestBPlusTreeDeleteByIndexSimple(t *testing.T) { 156 | context := &commonBPlusTreeTestContextStruct{t: t} 157 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 158 | metaTestDeleteByIndexSimple(t, context.tree) 159 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 160 | metaTestDeleteByIndexSimple(t, context.tree) 161 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 162 | metaTestDeleteByIndexSimple(t, context.tree) 163 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 164 | metaTestDeleteByIndexSimple(t, context.tree) 165 | } 166 | 167 | func TestBPlusTreeDeleteByKeySimple(t *testing.T) { 168 | context := &commonBPlusTreeTestContextStruct{t: t} 169 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 170 | metaTestDeleteByKeySimple(t, context.tree) 171 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 172 | metaTestDeleteByKeySimple(t, context.tree) 173 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 174 | metaTestDeleteByKeySimple(t, context.tree) 175 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 176 | metaTestDeleteByKeySimple(t, context.tree) 177 | } 178 | 179 | func TestBPlusTreeInsertGetDeleteByIndexTrivial(t *testing.T) { 180 | context := &commonBPlusTreeTestContextStruct{t: t} 181 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 182 | metaTestInsertGetDeleteByIndexTrivial(t, context.tree) 183 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 184 | metaTestInsertGetDeleteByIndexTrivial(t, context.tree) 185 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 186 | metaTestInsertGetDeleteByIndexTrivial(t, context.tree) 187 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 188 | metaTestInsertGetDeleteByIndexTrivial(t, context.tree) 189 | } 190 | 191 | func TestBPlusTreeInsertGetDeleteByIndexSmall(t *testing.T) { 192 | context := &commonBPlusTreeTestContextStruct{t: t} 193 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 194 | metaTestInsertGetDeleteByIndexSmall(t, context.tree) 195 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 196 | metaTestInsertGetDeleteByIndexSmall(t, context.tree) 197 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 198 | metaTestInsertGetDeleteByIndexSmall(t, context.tree) 199 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 200 | metaTestInsertGetDeleteByIndexSmall(t, context.tree) 201 | } 202 | 203 | func TestBPlusTreeInsertGetDeleteByIndexLarge(t *testing.T) { 204 | context := &commonBPlusTreeTestContextStruct{t: t} 205 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 206 | metaTestInsertGetDeleteByIndexLarge(t, context.tree) 207 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 208 | metaTestInsertGetDeleteByIndexLarge(t, context.tree) 209 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 210 | metaTestInsertGetDeleteByIndexLarge(t, context.tree) 211 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 212 | metaTestInsertGetDeleteByIndexLarge(t, context.tree) 213 | } 214 | 215 | func TestBPlusTreeInsertGetDeleteByIndexHuge(t *testing.T) { 216 | context := &commonBPlusTreeTestContextStruct{t: t} 217 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 218 | metaTestInsertGetDeleteByIndexHuge(t, context.tree) 219 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 220 | metaTestInsertGetDeleteByIndexHuge(t, context.tree) 221 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 222 | metaTestInsertGetDeleteByIndexHuge(t, context.tree) 223 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 224 | metaTestInsertGetDeleteByIndexHuge(t, context.tree) 225 | } 226 | 227 | func TestBPlusTreeInsertGetDeleteByKeyTrivial(t *testing.T) { 228 | context := &commonBPlusTreeTestContextStruct{t: t} 229 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 230 | metaTestInsertGetDeleteByKeyTrivial(t, context.tree) 231 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 232 | metaTestInsertGetDeleteByKeyTrivial(t, context.tree) 233 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 234 | metaTestInsertGetDeleteByKeyTrivial(t, context.tree) 235 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 236 | metaTestInsertGetDeleteByKeyTrivial(t, context.tree) 237 | } 238 | 239 | func TestBPlusTreeInsertGetDeleteByKeySmall(t *testing.T) { 240 | context := &commonBPlusTreeTestContextStruct{t: t} 241 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 242 | metaTestInsertGetDeleteByKeySmall(t, context.tree) 243 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 244 | metaTestInsertGetDeleteByKeySmall(t, context.tree) 245 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 246 | metaTestInsertGetDeleteByKeySmall(t, context.tree) 247 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 248 | metaTestInsertGetDeleteByKeySmall(t, context.tree) 249 | } 250 | 251 | func TestBPlusTreeInsertGetDeleteByKeyLarge(t *testing.T) { 252 | context := &commonBPlusTreeTestContextStruct{t: t} 253 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 254 | metaTestInsertGetDeleteByKeyLarge(t, context.tree) 255 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 256 | metaTestInsertGetDeleteByKeyLarge(t, context.tree) 257 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 258 | metaTestInsertGetDeleteByKeyLarge(t, context.tree) 259 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 260 | metaTestInsertGetDeleteByKeyLarge(t, context.tree) 261 | } 262 | 263 | func TestBPlusTreeInsertGetDeleteByKeyHuge(t *testing.T) { 264 | context := &commonBPlusTreeTestContextStruct{t: t} 265 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 266 | metaTestInsertGetDeleteByKeyHuge(t, context.tree) 267 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 268 | metaTestInsertGetDeleteByKeyHuge(t, context.tree) 269 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 270 | metaTestInsertGetDeleteByKeyHuge(t, context.tree) 271 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 272 | metaTestInsertGetDeleteByKeyHuge(t, context.tree) 273 | } 274 | 275 | func TestBPlusTreeBisect(t *testing.T) { 276 | context := &commonBPlusTreeTestContextStruct{t: t} 277 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxSmall, CompareInt, context, nil) 278 | metaTestBisect(t, context.tree) 279 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxModest, CompareInt, context, nil) 280 | metaTestBisect(t, context.tree) 281 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 282 | metaTestBisect(t, context.tree) 283 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxLarge, CompareInt, context, nil) 284 | metaTestBisect(t, context.tree) 285 | } 286 | 287 | func BenchmarkBPlusTreePut(b *testing.B) { 288 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 289 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 290 | metaBenchmarkPut(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 291 | } 292 | 293 | func BenchmarkBPlusTreeGetByIndex(b *testing.B) { 294 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 295 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 296 | metaBenchmarkGetByIndex(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 297 | } 298 | 299 | func BenchmarkBPlusTreePatchByIndex(b *testing.B) { 300 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 301 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 302 | metaBenchmarkPatchByIndex(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 303 | } 304 | 305 | func BenchmarkBPlusTreeDeleteByIndex(b *testing.B) { 306 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 307 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 308 | metaBenchmarkDeleteByIndex(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 309 | } 310 | 311 | func BenchmarkBPlusTreeGetByKey(b *testing.B) { 312 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 313 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 314 | metaBenchmarkGetByKey(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 315 | } 316 | 317 | func BenchmarkBPlusTreeBisectLeft(b *testing.B) { 318 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 319 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 320 | metaBenchmarkBisectLeft(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 321 | } 322 | 323 | func BenchmarkBPlusTreeBisectRight(b *testing.B) { 324 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 325 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 326 | metaBenchmarkBisectRight(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 327 | } 328 | 329 | func BenchmarkBPlusTreePatchByKey(b *testing.B) { 330 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 331 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 332 | metaBenchmarkPatchByKey(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 333 | } 334 | 335 | func BenchmarkBPlusTreeDeleteByKey(b *testing.B) { 336 | context := &commonBPlusTreeBenchmarkContextStruct{b: b} 337 | context.tree = NewBPlusTree(commonBPlusTreeTestNumKeysMaxTypical, CompareInt, context, nil) 338 | metaBenchmarkDeleteByKey(b, context.tree, commonBPlusTreeBenchmarkNumKeys) 339 | } 340 | -------------------------------------------------------------------------------- /btree_dump.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import "fmt" 7 | 8 | func (tree *btreeTreeStruct) Dump() (err error) { 9 | tree.Lock() 10 | defer tree.Unlock() 11 | 12 | if tree.nodeCache != nil { 13 | fmt.Printf("B+Tree @ %p has Node Cache @ %p\n", tree, tree.nodeCache) 14 | fmt.Printf(" .evictLowLimit = %v\n", tree.nodeCache.evictLowLimit) 15 | fmt.Printf(" .evictHighLimit = %v\n", tree.nodeCache.evictHighLimit) 16 | if tree.nodeCache.cleanLRUHead == nil { 17 | fmt.Printf(" .cleanLRUHead = nil\n") 18 | } else { 19 | fmt.Printf(" .cleanLRUHead = %p\n", tree.nodeCache.cleanLRUHead) 20 | } 21 | if tree.nodeCache.cleanLRUTail == nil { 22 | fmt.Printf(" .cleanLRUTail = nil\n") 23 | } else { 24 | fmt.Printf(" .cleanLRUTail = %p\n", tree.nodeCache.cleanLRUTail) 25 | } 26 | fmt.Printf(" .cleanLRUItems = %v\n", tree.nodeCache.cleanLRUItems) 27 | if tree.nodeCache.dirtyLRUHead == nil { 28 | fmt.Printf(" .dirtyLRUHead = nil\n") 29 | } else { 30 | fmt.Printf(" .dirtyLRUHead = %p\n", tree.nodeCache.dirtyLRUHead) 31 | } 32 | if tree.nodeCache.dirtyLRUTail == nil { 33 | fmt.Printf(" .dirtyLRUTail = nil\n") 34 | } else { 35 | fmt.Printf(" .dirtyLRUTail = %p\n", tree.nodeCache.dirtyLRUTail) 36 | } 37 | fmt.Printf(" .dirtyLRUItems = %v\n", tree.nodeCache.dirtyLRUItems) 38 | fmt.Printf(" .drainerActive = %v\n", tree.nodeCache.drainerActive) 39 | } 40 | 41 | fmt.Printf("B+Tree @ %p has Root Node @ %p\n", tree, tree.root) 42 | fmt.Printf(" .minKeysPerNode = %v\n", tree.minKeysPerNode) 43 | fmt.Printf(" .maxKeysPerNode = %v\n", tree.maxKeysPerNode) 44 | 45 | err = tree.dumpNode(tree.root, "") 46 | 47 | return 48 | } 49 | 50 | func (tree *btreeTreeStruct) dumpNode(node *btreeNodeStruct, indent string) (err error) { 51 | if !node.loaded { 52 | err = node.tree.loadNode(node) 53 | if err != nil { 54 | return 55 | } 56 | } 57 | 58 | if tree.nodeCache != nil { 59 | switch node.btreeNodeCacheElement.btreeNodeCacheTag { 60 | case noLRU: 61 | fmt.Printf("%v .btreeNodeCacheTag = noLRU (%v)\n", indent, noLRU) 62 | case cleanLRU: 63 | fmt.Printf("%v .btreeNodeCacheTag = cleanLRU (%v)\n", indent, cleanLRU) 64 | case dirtyLRU: 65 | fmt.Printf("%v .btreeNodeCacheTag = dirtyLRU (%v)\n", indent, dirtyLRU) 66 | default: 67 | fmt.Printf("%v .btreeNodeCacheTag = (%v)\n", indent, node.btreeNodeCacheElement.btreeNodeCacheTag) 68 | } 69 | 70 | if noLRU != node.btreeNodeCacheElement.btreeNodeCacheTag { 71 | if node.btreeNodeCacheElement.nextBTreeNode == nil { 72 | fmt.Printf("%v .nextBTreeNode = nil\n", indent) 73 | } else { 74 | fmt.Printf("%v .nextBTreeNode = %p\n", indent, node.btreeNodeCacheElement.nextBTreeNode) 75 | } 76 | if node.btreeNodeCacheElement.prevBTreeNode == nil { 77 | fmt.Printf("%v .prevBTreeNode = nil\n", indent) 78 | } else { 79 | fmt.Printf("%v .prevBTreeNode = %p\n", indent, node.btreeNodeCacheElement.prevBTreeNode) 80 | } 81 | } 82 | } 83 | 84 | fmt.Printf("%v .objectNumber = 0x%016x\n", indent, node.objectNumber) 85 | fmt.Printf("%v .objectOffset = 0x%016x\n", indent, node.objectOffset) 86 | fmt.Printf("%v .objectLength = 0x%016x\n", indent, node.objectLength) 87 | fmt.Printf("%v .items = %v\n", indent, node.items) 88 | fmt.Printf("%v .loaded = %v\n", indent, node.loaded) 89 | fmt.Printf("%v .dirty = %v\n", indent, node.dirty) 90 | fmt.Printf("%v .root = %v\n", indent, node.root) 91 | fmt.Printf("%v .leaf = %v\n", indent, node.leaf) 92 | 93 | if node.parentNode == nil { 94 | fmt.Printf("%v .parentNode = nil\n", indent) 95 | } else { 96 | fmt.Printf("%v .parentNode = %p\n", indent, node.parentNode) 97 | } 98 | 99 | if !node.leaf { 100 | if node.rootPrefixSumChild == nil { 101 | fmt.Printf("%v .rootPrefixSumChild = nil\n", indent) 102 | } else { 103 | fmt.Printf("%v .rootPrefixSumChild = %p\n", indent, node.rootPrefixSumChild) 104 | } 105 | } 106 | 107 | if !node.root { 108 | fmt.Printf("%v .prefixSumItems = %v\n", indent, node.prefixSumItems) 109 | fmt.Printf("%v .prefixSumKVIndex = %v\n", indent, node.prefixSumKVIndex) 110 | if node.prefixSumParent == nil { 111 | fmt.Printf("%v .prefixSumParent = nil\n", indent) 112 | } else { 113 | fmt.Printf("%v .prefixSumParent = %p\n", indent, node.prefixSumParent) 114 | } 115 | if node.prefixSumLeftChild == nil { 116 | fmt.Printf("%v .prefixSumLeftChild = nil\n", indent) 117 | } else { 118 | fmt.Printf("%v .prefixSumLeftChild = %p\n", indent, node.prefixSumLeftChild) 119 | } 120 | if node.prefixSumRightChild == nil { 121 | fmt.Printf("%v .prefixSumRightChild = nil\n", indent) 122 | } else { 123 | fmt.Printf("%v .prefixSumRightChild = %p\n", indent, node.prefixSumRightChild) 124 | } 125 | } 126 | 127 | if !node.leaf { 128 | if node.nonLeafLeftChild != nil { 129 | fmt.Printf("%v .nonLeafLeftChild = %p\n", indent, node.nonLeafLeftChild) 130 | tree.dumpNode(node.nonLeafLeftChild, " "+indent) 131 | } 132 | } 133 | 134 | numKVentries, lenErr := node.kvLLRB.Len() 135 | if lenErr != nil { 136 | err = lenErr 137 | return 138 | } 139 | for i := range numKVentries { 140 | key, value, _, getByIndexErr := node.kvLLRB.GetByIndex(i) 141 | if getByIndexErr != nil { 142 | err = getByIndexErr 143 | return 144 | } 145 | keyAsString, nonShadowingErr := tree.DumpKey(key) 146 | if nonShadowingErr != nil { 147 | err = nonShadowingErr 148 | return 149 | } 150 | fmt.Printf("%v .kvLLRB[%v].Key = %v\n", indent, i, keyAsString) 151 | if node.leaf { 152 | valueAsString, nonShadowingErr := tree.DumpValue(value) 153 | if nonShadowingErr != nil { 154 | err = nonShadowingErr 155 | return 156 | } 157 | fmt.Printf("%v .kvLLRB[%v].Value = %v\n", indent, i, valueAsString) 158 | } else { 159 | childNode := value.(*btreeNodeStruct) 160 | fmt.Printf("%v .kvLLRB[%v].Value = %p\n", indent, i, childNode) 161 | tree.dumpNode(childNode, " "+indent) 162 | } 163 | } 164 | 165 | err = nil 166 | 167 | return 168 | } 169 | -------------------------------------------------------------------------------- /btree_specific_api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "encoding/binary" 8 | "errors" 9 | "fmt" 10 | "testing" 11 | ) 12 | 13 | const specificBPlusTreeTestNumKeysMaxSmall = uint64(4) 14 | 15 | type logSegmentChunkStruct struct { 16 | startingOffset uint64 17 | chunkByteSlice []byte 18 | } 19 | 20 | type specificBPlusTreeTestContextStruct struct { 21 | t *testing.T 22 | lastLogSegmentNumberGenerated uint64 23 | lastLogOffsetGenerated uint64 24 | logSegmentChunkMap map[uint64]*logSegmentChunkStruct // Key == logSegmentNumber (only 1 chunk stored per LogSegment) 25 | } 26 | 27 | type valueStruct struct { 28 | u32 uint32 29 | s8 [8]byte 30 | } 31 | 32 | func (context *specificBPlusTreeTestContextStruct) GetNode(logSegmentNumber, logOffset, logLength uint64) (nodeByteSlice []byte, err error) { 33 | logSegmentChunk, ok := context.logSegmentChunkMap[logSegmentNumber] 34 | 35 | if !ok { 36 | err = errors.New("logSegmentNumber not found") 37 | return 38 | } 39 | 40 | if logSegmentChunk.startingOffset != logOffset { 41 | err = errors.New("logOffset not found") 42 | return 43 | } 44 | 45 | if uint64(len(logSegmentChunk.chunkByteSlice)) != logLength { 46 | err = errors.New("logLength not found") 47 | return 48 | } 49 | 50 | nodeByteSlice = logSegmentChunk.chunkByteSlice 51 | 52 | err = nil 53 | return 54 | } 55 | 56 | func (context *specificBPlusTreeTestContextStruct) PutNode(nodeByteSlice []byte) (logSegmentNumber, logOffset uint64, err error) { 57 | context.lastLogSegmentNumberGenerated++ 58 | logSegmentNumber = context.lastLogSegmentNumberGenerated 59 | 60 | context.lastLogOffsetGenerated += logSegmentNumber + uint64(len(nodeByteSlice)) 61 | logOffset = context.lastLogOffsetGenerated 62 | 63 | logSegmentChunk := &logSegmentChunkStruct{ 64 | startingOffset: logOffset, 65 | chunkByteSlice: nodeByteSlice, 66 | } 67 | 68 | context.logSegmentChunkMap[logSegmentNumber] = logSegmentChunk 69 | 70 | err = nil 71 | return 72 | } 73 | 74 | func (context *specificBPlusTreeTestContextStruct) DiscardNode(logSegmentNumber, logOffset, logLength uint64) (err error) { 75 | logSegmentChunk, ok := context.logSegmentChunkMap[logSegmentNumber] 76 | if !ok { 77 | err = errors.New("logSegmentNumber not found") 78 | return 79 | } 80 | 81 | if logSegmentChunk.startingOffset != logOffset { 82 | err = errors.New("logOffset not found") 83 | return 84 | } 85 | 86 | if uint64(len(logSegmentChunk.chunkByteSlice)) != logLength { 87 | err = errors.New("logLength not found") 88 | return 89 | } 90 | 91 | delete(context.logSegmentChunkMap, logSegmentNumber) 92 | 93 | err = nil 94 | return 95 | } 96 | 97 | func (context *specificBPlusTreeTestContextStruct) DumpKey(key Key) (keyAsString string, err error) { 98 | keyAsUint32, ok := key.(uint32) 99 | if !ok { 100 | context.t.Fatalf("DumpKey() argument not an uint32") 101 | } 102 | keyAsString = fmt.Sprintf("0x%08X", keyAsUint32) 103 | err = nil 104 | return 105 | } 106 | 107 | func (context *specificBPlusTreeTestContextStruct) PackKey(key Key) (packedKey []byte, err error) { 108 | keyAsUint32, ok := key.(uint32) 109 | if !ok { 110 | context.t.Fatalf("PackKey() argument not a uint32") 111 | } 112 | packedKey = make([]byte, 4) 113 | binary.LittleEndian.PutUint32(packedKey, keyAsUint32) 114 | err = nil 115 | return 116 | } 117 | 118 | func (context *specificBPlusTreeTestContextStruct) UnpackKey(packedKey []byte) (key Key, bytesConsumed uint64, err error) { 119 | if 4 > len(packedKey) { 120 | context.t.Fatalf("UnpackKey() called with insufficient packedKey size") 121 | } 122 | keyAsUint32 := binary.LittleEndian.Uint32(packedKey[:4]) 123 | key = keyAsUint32 124 | bytesConsumed = 4 125 | err = nil 126 | return 127 | } 128 | 129 | func (context *specificBPlusTreeTestContextStruct) DumpValue(value Value) (valueAsString string, err error) { 130 | valueAsValueStruct, ok := value.(valueStruct) 131 | if !ok { 132 | context.t.Fatalf("DumpValue() argument not a valueStruct") 133 | } 134 | valueAsString = fmt.Sprintf( 135 | "{u32: 0x%08X, s8: 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X 0x%02X}", 136 | valueAsValueStruct.u32, 137 | valueAsValueStruct.s8[0], 138 | valueAsValueStruct.s8[1], 139 | valueAsValueStruct.s8[2], 140 | valueAsValueStruct.s8[3], 141 | valueAsValueStruct.s8[4], 142 | valueAsValueStruct.s8[5], 143 | valueAsValueStruct.s8[6], 144 | valueAsValueStruct.s8[7]) 145 | err = nil 146 | return 147 | } 148 | 149 | func (context *specificBPlusTreeTestContextStruct) PackValue(value Value) (packedValue []byte, err error) { 150 | valueAsValueStruct, ok := value.(valueStruct) 151 | if !ok { 152 | context.t.Fatalf("PackValue() argument not a valueStruct") 153 | } 154 | u32Packed := make([]byte, 4) 155 | binary.LittleEndian.PutUint32(u32Packed, valueAsValueStruct.u32) 156 | packedValue = make([]byte, 0, 12) 157 | packedValue = append(packedValue, u32Packed...) 158 | packedValue = append(packedValue, valueAsValueStruct.s8[:]...) 159 | err = nil 160 | return 161 | } 162 | 163 | func (context *specificBPlusTreeTestContextStruct) UnpackValue(packedValue []byte) (value Value, bytesConsumed uint64, err error) { 164 | if 12 > len(packedValue) { 165 | context.t.Fatalf("UnpackValue() called with insufficient packedValue size") 166 | } 167 | valueAsUint32 := binary.LittleEndian.Uint32(packedValue[:4]) 168 | var s8AsArray [8]byte 169 | copy(s8AsArray[:], packedValue[4:12]) 170 | value = valueStruct{u32: valueAsUint32, s8: s8AsArray} 171 | bytesConsumed = 12 172 | err = nil 173 | return 174 | } 175 | 176 | func uint32To8ReplicaByteArray(u32 uint32) (b8 [8]byte) { 177 | // Assumes u32 < 0x100 178 | 179 | for i := range 8 { 180 | b8[i] = byte(u32) 181 | } 182 | 183 | return 184 | } 185 | 186 | func TestBPlusTreeSpecific(t *testing.T) { 187 | var ( 188 | btreeCacheNew BPlusTreeCache 189 | btreeCacheOld BPlusTreeCache 190 | btreeLen int 191 | btreeNew BPlusTree 192 | btreeOld BPlusTree 193 | err error 194 | layoutReportExpected LayoutReport 195 | layoutReportReturned LayoutReport 196 | logSegmentBytesExpected uint64 197 | logSegmentBytesReturned uint64 198 | logSegmentChunk *logSegmentChunkStruct 199 | logSegmentNumber uint64 200 | ok bool 201 | persistentContext *specificBPlusTreeTestContextStruct 202 | rootObjectNumberFromFetch uint64 203 | rootObjectNumberFromFlush uint64 204 | rootObjectOffsetFromFetch uint64 205 | rootObjectOffsetFromFlush uint64 206 | rootObjectLengthFromFetch uint64 207 | rootObjectLengthFromFlush uint64 208 | valueAsValueStructExpected valueStruct 209 | valueAsValueStructReturned valueStruct 210 | valueAsValueStructToInsert valueStruct 211 | valueAsValueReturned Value 212 | ) 213 | 214 | persistentContext = &specificBPlusTreeTestContextStruct{t: t, lastLogSegmentNumberGenerated: 0, lastLogOffsetGenerated: 0, logSegmentChunkMap: make(map[uint64]*logSegmentChunkStruct)} 215 | 216 | btreeCacheNew = NewBPlusTreeCache(100, 200) 217 | 218 | btreeNew = NewBPlusTree(specificBPlusTreeTestNumKeysMaxSmall, CompareUint32, persistentContext, btreeCacheNew) 219 | 220 | rootObjectNumberFromFetch, rootObjectOffsetFromFetch, rootObjectLengthFromFetch = btreeNew.FetchLocation() 221 | if uint64(0) != rootObjectNumberFromFetch { 222 | t.Fatalf("btreeNew.FetchLocation() returned non-zero rootObjectNumber") 223 | } 224 | if uint64(0) != rootObjectOffsetFromFetch { 225 | t.Fatalf("btreeNew.FetchLocation() returned non-zero rootObjectOffset") 226 | } 227 | if uint64(0) != rootObjectLengthFromFetch { 228 | t.Fatalf("btreeNew.FetchLocation() returned non-zero rootObjectLength") 229 | } 230 | 231 | valueAsValueStructToInsert = valueStruct{u32: 5, s8: uint32To8ReplicaByteArray(5)} 232 | ok, err = btreeNew.Put(uint32(5), valueAsValueStructToInsert) 233 | if err != nil { 234 | t.Fatalf("btreeNew.Put(uint32(5) should not have failed") 235 | } 236 | if !ok { 237 | t.Fatalf("btreeNew.Put(uint32(5), valueAsValueStructToInsert).ok should have been true") 238 | } 239 | 240 | valueAsValueStructToInsert = valueStruct{u32: 3, s8: uint32To8ReplicaByteArray(3)} 241 | ok, err = btreeNew.Put(uint32(3), valueAsValueStructToInsert) 242 | if err != nil { 243 | t.Fatalf("btreeNew.Put(uint32(3) should not have failed") 244 | } 245 | if !ok { 246 | t.Fatalf("btreeNew.Put(uint32(3), valueAsValueStructToInsert).ok should have been true") 247 | } 248 | 249 | valueAsValueStructToInsert = valueStruct{u32: 7, s8: uint32To8ReplicaByteArray(7)} 250 | ok, err = btreeNew.Put(uint32(7), valueAsValueStructToInsert) 251 | if err != nil { 252 | t.Fatalf("btreeNew.Put(uint32(7) should not have failed") 253 | } 254 | if !ok { 255 | t.Fatalf("btreeNew.Put(uint32(7), valueAsValueStructToInsert)).ok should have been true") 256 | } 257 | 258 | rootObjectNumberFromFlush, rootObjectOffsetFromFlush, rootObjectLengthFromFlush, err = btreeNew.Flush(false) 259 | if err != nil { 260 | t.Fatalf("btreeNew.Flush(false) should not have failed") 261 | } 262 | 263 | rootObjectNumberFromFetch, rootObjectOffsetFromFetch, rootObjectLengthFromFetch = btreeNew.FetchLocation() 264 | if rootObjectNumberFromFlush != rootObjectNumberFromFetch { 265 | t.Fatalf("btreeNew.FetchLocation() returned unexpected rootObjectNumber") 266 | } 267 | if rootObjectOffsetFromFlush != rootObjectOffsetFromFetch { 268 | t.Fatalf("btreeNew.FetchLocation() returned unexpected rootObjectOffset") 269 | } 270 | if rootObjectLengthFromFlush != rootObjectLengthFromFetch { 271 | t.Fatalf("btreeNew.FetchLocation() returned unexpected rootObjectLength") 272 | } 273 | 274 | valueAsValueReturned, ok, err = btreeNew.GetByKey(uint32(5)) 275 | if err != nil { 276 | t.Fatalf("btreeNew.GetByKey(uint32(5)) should not have failed") 277 | } 278 | if !ok { 279 | t.Fatalf("btreeNew.GetByKey(uint32(5)).ok should have been true") 280 | } 281 | valueAsValueStructReturned = valueAsValueReturned.(valueStruct) 282 | valueAsValueStructExpected = valueStruct{u32: 5, s8: uint32To8ReplicaByteArray(5)} 283 | if valueAsValueStructReturned != valueAsValueStructExpected { 284 | t.Fatalf("btreeNew.GetByKey(uint32(5)).value should have been valueAsValueStructExpected") 285 | } 286 | 287 | rootObjectNumberFromFlush, rootObjectOffsetFromFlush, rootObjectLengthFromFlush, err = btreeNew.Flush(true) 288 | if err != nil { 289 | t.Fatalf("btreeNew.Flush(true) should not have failed") 290 | } 291 | 292 | valueAsValueReturned, ok, err = btreeNew.GetByKey(uint32(3)) 293 | if err != nil { 294 | t.Fatalf("btreeNew.GetByKey(uint32(3)) should not have failed") 295 | } 296 | if !ok { 297 | t.Fatalf("btreeNew.GetByKey(uint32(3)).ok should have been true") 298 | } 299 | valueAsValueStructReturned = valueAsValueReturned.(valueStruct) 300 | valueAsValueStructExpected = valueStruct{u32: 3, s8: uint32To8ReplicaByteArray(3)} 301 | if valueAsValueStructReturned != valueAsValueStructExpected { 302 | t.Fatalf("btreeNew.GetByKey(uint32(3)).value should have been valueAsValueStructExpected") 303 | } 304 | 305 | layoutReportExpected = make(map[uint64]uint64) 306 | for logSegmentNumber, logSegmentChunk = range persistentContext.logSegmentChunkMap { 307 | logSegmentBytesExpected = uint64(len(logSegmentChunk.chunkByteSlice)) 308 | layoutReportExpected[logSegmentNumber] = logSegmentBytesExpected // Note: assumes no chunks are stale 309 | } 310 | layoutReportReturned, err = btreeNew.FetchLayoutReport() 311 | if err != nil { 312 | t.Fatalf("btreeNew.FetchLayoutReport() should not have failed") 313 | } 314 | if len(layoutReportExpected) != len(layoutReportReturned) { 315 | t.Fatalf("btreeNew.FetchLayoutReport() returned unexpected LayoutReport") 316 | } 317 | for logSegmentNumber, logSegmentBytesReturned = range layoutReportReturned { 318 | logSegmentBytesExpected, ok = layoutReportExpected[logSegmentNumber] 319 | if (!ok) || (logSegmentBytesExpected != logSegmentBytesReturned) { 320 | t.Fatalf("btreeNew.FetchLayoutReport() returned unexpected LayoutReport") 321 | } 322 | } 323 | 324 | btreeCacheNew.UpdateLimits(200, 300) 325 | 326 | err = btreeNew.Purge(true) 327 | if err != nil { 328 | t.Fatalf("btreeNew.Purge(true) should not have failed") 329 | } 330 | 331 | valueAsValueReturned, ok, err = btreeNew.GetByKey(uint32(7)) 332 | if err != nil { 333 | t.Fatalf("btreeNew.GetByKey(uint32(7)) should not have failed") 334 | } 335 | if !ok { 336 | t.Fatalf("btreeNew.GetByKey(uint32(7)).ok should have been true") 337 | } 338 | valueAsValueStructReturned = valueAsValueReturned.(valueStruct) 339 | valueAsValueStructExpected = valueStruct{u32: 7, s8: uint32To8ReplicaByteArray(7)} 340 | if valueAsValueStructReturned != valueAsValueStructExpected { 341 | t.Fatalf("btreeNew.GetByKey(uint32(3)).value should have been valueAsValueStructExpected") 342 | } 343 | 344 | btreeNew = nil // Just let Go Garbage Collection have it (similating a crash/restart) 345 | 346 | btreeCacheOld = NewBPlusTreeCache(100, 200) 347 | 348 | btreeOld, err = OldBPlusTree(rootObjectNumberFromFlush, rootObjectOffsetFromFlush, rootObjectLengthFromFlush, CompareUint32, persistentContext, btreeCacheOld) 349 | if err != nil { 350 | t.Fatalf("OldBPlusTree() should not have failed") 351 | } 352 | 353 | btreeLen, err = btreeOld.Len() 354 | if err != nil { 355 | t.Fatalf("btreeOld.Len() should not have failed") 356 | } 357 | if btreeLen != 3 { 358 | t.Fatalf("btreeOld.Len() should have been 3") 359 | } 360 | 361 | valueAsValueReturned, ok, err = btreeOld.GetByKey(uint32(5)) 362 | if err != nil { 363 | t.Fatalf("btreeOld.GetByKey(uint32(5)) should not have failed") 364 | } 365 | if !ok { 366 | t.Fatalf("btreeOld.GetByKey(uint32(5)).ok should have been true") 367 | } 368 | valueAsValueStructReturned = valueAsValueReturned.(valueStruct) 369 | valueAsValueStructExpected = valueStruct{u32: 5, s8: uint32To8ReplicaByteArray(5)} 370 | if valueAsValueStructReturned != valueAsValueStructExpected { 371 | t.Fatalf("btreeOld.GetByKey(uint32(5)).value should have been valueAsValueStructExpected") 372 | } 373 | 374 | valueAsValueReturned, ok, err = btreeOld.GetByKey(uint32(3)) 375 | if err != nil { 376 | t.Fatalf("btreeOld.GetByKey(uint32(3)) should not have failed") 377 | } 378 | if !ok { 379 | t.Fatalf("btreeOld.GetByKey(uint32(3)).ok should have been true") 380 | } 381 | valueAsValueStructReturned = valueAsValueReturned.(valueStruct) 382 | valueAsValueStructExpected = valueStruct{u32: 3, s8: uint32To8ReplicaByteArray(3)} 383 | if valueAsValueStructReturned != valueAsValueStructExpected { 384 | t.Fatalf("btreeOld.GetByKey(uint32(3)).value should have been valueAsValueStructExpected") 385 | } 386 | 387 | valueAsValueReturned, ok, err = btreeOld.GetByKey(uint32(7)) 388 | if err != nil { 389 | t.Fatalf("btreeOld.GetByKey(uint32(7)) should not have failed") 390 | } 391 | if !ok { 392 | t.Fatalf("btreeOld.GetByKey(uint32(7)).ok should have been true") 393 | } 394 | valueAsValueStructReturned = valueAsValueReturned.(valueStruct) 395 | valueAsValueStructExpected = valueStruct{u32: 7, s8: uint32To8ReplicaByteArray(7)} 396 | if valueAsValueStructReturned != valueAsValueStructExpected { 397 | t.Fatalf("btreeOld.GetByKey(uint32(3)).value should have been valueAsValueStructExpected") 398 | } 399 | 400 | err = btreeOld.Touch() 401 | if err != nil { 402 | t.Fatalf("btreeOld.Touch() should not have failed") 403 | } 404 | 405 | err = btreeOld.Purge(false) 406 | if err != nil { 407 | t.Fatalf("btreeOld.Purge(false) [case 1] should not have failed") 408 | } 409 | 410 | err = btreeOld.Purge(true) 411 | if err == nil { 412 | t.Fatalf("btreeOld.Purge(true) [case 1] should have failed") 413 | } 414 | 415 | valueAsValueStructToInsert = valueStruct{u32: 2, s8: uint32To8ReplicaByteArray(2)} 416 | ok, err = btreeOld.Put(uint32(2), valueAsValueStructToInsert) 417 | if err != nil { 418 | t.Fatalf("btreeOld.Put(uint32(2) should not have failed") 419 | } 420 | if !ok { 421 | t.Fatalf("btreeOld.Put(uint32(2), valueAsValueStructToInsert).ok should have been true") 422 | } 423 | 424 | valueAsValueStructToInsert = valueStruct{u32: 4, s8: uint32To8ReplicaByteArray(4)} 425 | ok, err = btreeOld.Put(uint32(4), valueAsValueStructToInsert) 426 | if err != nil { 427 | t.Fatalf("btreeOld.Put(uint32(4) should not have failed") 428 | } 429 | if !ok { 430 | t.Fatalf("btreeOld.Put(uint32(4), valueAsValueStructToInsert).ok should have been true") 431 | } 432 | 433 | valueAsValueStructToInsert = valueStruct{u32: 6, s8: uint32To8ReplicaByteArray(6)} 434 | ok, err = btreeOld.Put(uint32(6), valueAsValueStructToInsert) 435 | if err != nil { 436 | t.Fatalf("btreeOld.Put(uint32(6) should not have failed") 437 | } 438 | if !ok { 439 | t.Fatalf("btreeOld.Put(uint32(6), valueAsValueStructToInsert).ok should have been true") 440 | } 441 | 442 | valueAsValueStructToInsert = valueStruct{u32: 8, s8: uint32To8ReplicaByteArray(8)} 443 | ok, err = btreeOld.Put(uint32(8), valueAsValueStructToInsert) 444 | if err != nil { 445 | t.Fatalf("btreeOld.Put(uint32(8) should not have failed") 446 | } 447 | if !ok { 448 | t.Fatalf("btreeOld.Put(uint32(8), valueAsValueStructToInsert).ok should have been true") 449 | } 450 | 451 | err = btreeOld.Purge(false) 452 | if err != nil { 453 | t.Fatalf("btreeOld.Purge(false) [case 2] should not have failed") 454 | } 455 | 456 | err = btreeOld.Purge(true) 457 | if err == nil { 458 | t.Fatalf("btreeOld.Purge(true) [case 2] should have failed") 459 | } 460 | 461 | nextItemIndexToTouch, err := btreeOld.TouchItem(0) 462 | if err != nil { 463 | t.Fatalf("btreeOld.TouchItem(0) should not have failed") 464 | } 465 | if nextItemIndexToTouch != 2 { 466 | t.Fatalf("btreeOld.TouchItem(0) should have returned 2") 467 | } 468 | 469 | nextItemIndexToTouch, err = btreeOld.TouchItem(2) 470 | if err != nil { 471 | t.Fatalf("btreeOld.TouchItem(2) should not have failed") 472 | } 473 | if nextItemIndexToTouch != 4 { 474 | t.Fatalf("btreeOld.TouchItem(2) should have returned 4") 475 | } 476 | 477 | nextItemIndexToTouch, err = btreeOld.TouchItem(4) 478 | if err != nil { 479 | t.Fatalf("btreeOld.TouchItem(4) should not have failed") 480 | } 481 | if nextItemIndexToTouch != 7 { 482 | t.Fatalf("btreeOld.TouchItem(4) should have returned 7") 483 | } 484 | 485 | nextItemIndexToTouch, err = btreeOld.TouchItem(7) 486 | if err != nil { 487 | t.Fatalf("btreeOld.TouchItem(7) should not have failed") 488 | } 489 | if nextItemIndexToTouch != 0 { 490 | t.Fatalf("btreeOld.TouchItem(7) should have returned 2") 491 | } 492 | 493 | err = btreeOld.Discard() 494 | if err != nil { 495 | t.Fatalf("btreeOld.Discard() should not have failed") 496 | } 497 | } 498 | -------------------------------------------------------------------------------- /btree_validate.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "errors" 8 | "fmt" 9 | ) 10 | 11 | func (tree *btreeTreeStruct) Validate() (err error) { 12 | tree.Lock() 13 | defer tree.Unlock() 14 | 15 | err = tree.root.validate() 16 | 17 | return 18 | } 19 | 20 | func (node *btreeNodeStruct) validate() (err error) { 21 | if !node.loaded { 22 | err = node.tree.loadNode(node) 23 | if err != nil { 24 | return 25 | } 26 | } 27 | 28 | if node.leaf { 29 | err = node.kvLLRB.Validate() 30 | if err != nil { 31 | return 32 | } 33 | 34 | numKeysInLLRB, nonShadowingErr := node.kvLLRB.Len() 35 | if nonShadowingErr != nil { 36 | err = nonShadowingErr 37 | return 38 | } 39 | 40 | if numKeysInLLRB != int(node.items) { 41 | err = fmt.Errorf("leaf node @%p items [%d] != node.kvLLRB.Len() [%d]", node, numKeysInLLRB, node.items) 42 | return 43 | } 44 | 45 | if !node.root && (uint64(numKeysInLLRB) < node.tree.minKeysPerNode) { 46 | err = fmt.Errorf("non-root Leaf node @%p kvLLRB.Len() [%d] < node.tree.minKeysPerNode [%d]", node, numKeysInLLRB, node.tree.minKeysPerNode) 47 | return 48 | } 49 | if uint64(numKeysInLLRB) > node.tree.maxKeysPerNode { 50 | err = fmt.Errorf("leaf node @%p kvLLRB.Len() [%d] > node.tree.maxKeysPerNode [%d]", node, numKeysInLLRB, node.tree.maxKeysPerNode) 51 | return 52 | } 53 | } else { 54 | childItems := uint64(0) 55 | 56 | if node.nonLeafLeftChild != nil { 57 | if node.nonLeafLeftChild.parentNode != node { 58 | err = fmt.Errorf("node @%p had nonLeafLeftChild @%p with unexpected .parentNode %p", node, node.nonLeafLeftChild, node.nonLeafLeftChild.parentNode) 59 | return 60 | } 61 | 62 | err = node.nonLeafLeftChild.validate() 63 | if err != nil { 64 | return 65 | } 66 | 67 | childItems += node.nonLeafLeftChild.items 68 | } 69 | 70 | err = node.kvLLRB.Validate() 71 | if err != nil { 72 | return 73 | } 74 | 75 | numChildrenInLLRB, nonShadowingErr := node.kvLLRB.Len() 76 | if nonShadowingErr != nil { 77 | err = nonShadowingErr 78 | return 79 | } 80 | 81 | if !node.root && (uint64(numChildrenInLLRB) < node.tree.minKeysPerNode) { 82 | err = fmt.Errorf("Non-Root Non-Leaf node @%p kvLLRB.Len() [%d] < node.tree.minKeysPerNode [%d]", node, numChildrenInLLRB, node.tree.minKeysPerNode) 83 | return 84 | } 85 | if uint64(numChildrenInLLRB) > node.tree.maxKeysPerNode { 86 | err = fmt.Errorf("Non-Leaf node @%p kvLLRB.Len() [%d] > node.tree.maxKeysPerNode [%d]", node, numChildrenInLLRB, node.tree.maxKeysPerNode) 87 | return 88 | } 89 | 90 | for i := range numChildrenInLLRB { 91 | _, childNodeAsValue, ok, nonShadowingErr := node.kvLLRB.GetByIndex(i) 92 | if nonShadowingErr != nil { 93 | err = nonShadowingErr 94 | return 95 | } 96 | if !ok { 97 | err = errors.New("logic error: validate() had indexing problem in kvLLRB") 98 | return 99 | } 100 | 101 | childNode := childNodeAsValue.(*btreeNodeStruct) 102 | 103 | if childNode.parentNode != node { 104 | err = fmt.Errorf("node @%p had childNode @%p with unexpected .parentNode %p", node, childNode, childNode.parentNode) 105 | return 106 | } 107 | 108 | err = childNode.validate() 109 | if err != nil { 110 | return 111 | } 112 | 113 | childItems += childNode.items 114 | } 115 | 116 | if childItems != node.items { 117 | err = fmt.Errorf("Non-Leaf node @%p items [%d] != sum of childNode's .items [%d]", node, childItems, node.items) 118 | return 119 | } 120 | 121 | if node.rootPrefixSumChild == nil { 122 | err = fmt.Errorf("Non-Leaf node @%p rootPrefixSumChild == nil", node) 123 | return 124 | } 125 | 126 | if node.rootPrefixSumChild.prefixSumParent != nil { 127 | err = fmt.Errorf("Non-Leaf node @%p rootPrefixSumChild.prefixSumParent != nil", node) 128 | return 129 | } 130 | 131 | if childItems != node.rootPrefixSumChild.prefixSumItems { 132 | err = fmt.Errorf("Non-Leaf node @%p rootPrefixSumChild.prefixSumItems (%v) expected to be %v [case 1]", node, node.rootPrefixSumChild.prefixSumItems, childItems) 133 | return 134 | } 135 | 136 | err = node.rootPrefixSumChild.validatePrefixSum() 137 | if err != nil { 138 | return 139 | } 140 | } 141 | 142 | err = nil 143 | 144 | return 145 | } 146 | 147 | func (node *btreeNodeStruct) validatePrefixSum() (err error) { 148 | expectedPrefixSumItems := node.items 149 | 150 | if node.prefixSumLeftChild != nil { 151 | expectedPrefixSumItems += node.prefixSumLeftChild.prefixSumItems 152 | 153 | if node != node.prefixSumLeftChild.prefixSumParent { 154 | err = fmt.Errorf("Non-Leaf node @%p non-rootPrefixSumChild.prefixSumParent mismatch [case 1]", node) 155 | return 156 | } 157 | 158 | err = node.prefixSumLeftChild.validatePrefixSum() 159 | if err != nil { 160 | return 161 | } 162 | } 163 | 164 | if node.prefixSumRightChild != nil { 165 | expectedPrefixSumItems += node.prefixSumRightChild.prefixSumItems 166 | 167 | if node != node.prefixSumRightChild.prefixSumParent { 168 | err = fmt.Errorf("Non-Leaf node @%p non-rootPrefixSumChild.prefixSumParent mismatch [case 2]", node) 169 | return 170 | } 171 | 172 | err = node.prefixSumLeftChild.validatePrefixSum() 173 | if err != nil { 174 | return 175 | } 176 | } 177 | 178 | if expectedPrefixSumItems != node.prefixSumItems { 179 | err = fmt.Errorf("Non-Leaf node @%p rootPrefixSumChild.prefixSumItems (%v) expected to be %v [case 2]", node, node.prefixSumItems, expectedPrefixSumItems) 180 | return 181 | } 182 | 183 | err = nil 184 | 185 | return 186 | } 187 | -------------------------------------------------------------------------------- /common_api.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | // Package sortedmap provides sorted maps implemented as either a Left-Leaning Red-Black Tree (in memory) or a B+Tree (pageable) 5 | package sortedmap 6 | 7 | import ( 8 | "bytes" 9 | "errors" 10 | "strings" 11 | "time" 12 | ) 13 | 14 | type Key interface{} 15 | type Value interface{} 16 | 17 | type Compare func(key1 Key, key2 Key) (result int, err error) // returns <0 if key1 < key2, 0 if key1 == key2, >0 if key1 > key2 18 | 19 | func CompareInt(key1, key2 Key) (result int, err error) { 20 | key1Int, ok := key1.(int) 21 | if !ok { 22 | err = errors.New("CompareInt(non-int,) not supported") 23 | return 24 | } 25 | key2Int, ok := key2.(int) 26 | if !ok { 27 | err = errors.New("CompareInt(int, non-int) not supported") 28 | return 29 | } 30 | 31 | result = key1Int - key2Int 32 | err = nil 33 | 34 | return 35 | } 36 | 37 | func CompareUint16(key1, key2 Key) (result int, err error) { 38 | key1Uint16, ok := key1.(uint16) 39 | if !ok { 40 | err = errors.New("CompareUint16(non-uint16,) not supported") 41 | return 42 | } 43 | key2Uint16, ok := key2.(uint16) 44 | if !ok { 45 | err = errors.New("CompareUint16(uint16, non-uint16) not supported") 46 | return 47 | } 48 | 49 | switch { 50 | case key1Uint16 < key2Uint16: 51 | result = -1 52 | case key1Uint16 == key2Uint16: 53 | result = 0 54 | default: // key1Uint16 > key2Uint16 55 | result = 1 56 | } 57 | 58 | err = nil 59 | 60 | return 61 | } 62 | 63 | func CompareUint32(key1, key2 Key) (result int, err error) { 64 | key1Uint32, ok := key1.(uint32) 65 | if !ok { 66 | err = errors.New("CompareUint32(non-uint32,) not supported") 67 | return 68 | } 69 | key2Uint32, ok := key2.(uint32) 70 | if !ok { 71 | err = errors.New("CompareUint32(uint32, non-uint32) not supported") 72 | return 73 | } 74 | 75 | switch { 76 | case key1Uint32 < key2Uint32: 77 | result = -1 78 | case key1Uint32 == key2Uint32: 79 | result = 0 80 | default: // key1Uint32 > key2Uint32 81 | result = 1 82 | } 83 | 84 | err = nil 85 | 86 | return 87 | } 88 | 89 | func CompareUint64(key1, key2 Key) (result int, err error) { 90 | key1Uint64, ok := key1.(uint64) 91 | if !ok { 92 | err = errors.New("CompareUint64(non-uint64,) not supported") 93 | return 94 | } 95 | key2Uint64, ok := key2.(uint64) 96 | if !ok { 97 | err = errors.New("CompareUint64(uint64, non-uint64) not supported") 98 | return 99 | } 100 | 101 | switch { 102 | case key1Uint64 < key2Uint64: 103 | result = -1 104 | case key1Uint64 == key2Uint64: 105 | result = 0 106 | default: // key1Uint64 > key2Uint64 107 | result = 1 108 | } 109 | 110 | err = nil 111 | 112 | return 113 | } 114 | 115 | func CompareString(key1, key2 Key) (result int, err error) { 116 | key1String, ok := key1.(string) 117 | if !ok { 118 | err = errors.New("CompareString(non-string,) not supported") 119 | return 120 | } 121 | key2String, ok := key2.(string) 122 | if !ok { 123 | err = errors.New("CompareString(string, non-string) not supported") 124 | return 125 | } 126 | 127 | result = strings.Compare(key1String, key2String) 128 | err = nil 129 | 130 | return 131 | } 132 | 133 | func CompareByteSlice(key1, key2 Key) (result int, err error) { 134 | key1Slice, ok := key1.([]byte) 135 | if !ok { 136 | err = errors.New("CompareByteSlice(non-[]byte,) not supported") 137 | return 138 | } 139 | key2Slice, ok := key2.([]byte) 140 | if !ok { 141 | err = errors.New("CompareByteSlice([]byte, non-[]byte) not supported") 142 | return 143 | } 144 | 145 | result = bytes.Compare(key1Slice, key2Slice) 146 | err = nil 147 | 148 | return 149 | } 150 | 151 | func CompareTime(key1, key2 Key) (result int, err error) { 152 | key1Time, ok := key1.(time.Time) 153 | if !ok { 154 | err = errors.New("CompareTime(non-time.Time,) not supported") 155 | return 156 | } 157 | key2Time, ok := key2.(time.Time) 158 | if !ok { 159 | err = errors.New("CompareTime(time.Time, non-time.Time) not supported") 160 | return 161 | } 162 | 163 | switch { 164 | case key1Time.Before(key2Time): 165 | result = -1 166 | case key1Time.After(key2Time): 167 | result = 1 168 | default: // key1Time == key2Time 169 | result = 0 170 | } 171 | 172 | err = nil 173 | 174 | return 175 | } 176 | 177 | type SortedMap interface { 178 | BisectLeft(key Key) (index int, found bool, err error) // Returns index of matching key:value pair or, if no match, index is to key:value just before where this key would go 179 | BisectRight(key Key) (index int, found bool, err error) // Returns index of matching key:value pair or, if no match, index is to key:value just after where this key would go 180 | DeleteByIndex(index int) (ok bool, err error) 181 | DeleteByKey(key Key) (ok bool, err error) 182 | Dump() (err error) 183 | GetByIndex(index int) (key Key, value Value, ok bool, err error) 184 | GetByKey(key Key) (value Value, ok bool, err error) 185 | Len() (numberOfItems int, err error) 186 | PatchByIndex(index int, value Value) (ok bool, err error) 187 | PatchByKey(key Key, value Value) (ok bool, err error) 188 | Put(key Key, value Value) (ok bool, err error) 189 | Validate() (err error) 190 | } 191 | 192 | // DumpCallbacks specifies the interface to a set of callbacks provided by the client 193 | type DumpCallbacks interface { 194 | DumpKey(key Key) (keyAsString string, err error) 195 | DumpValue(value Value) (valueAsString string, err error) 196 | } 197 | -------------------------------------------------------------------------------- /common_api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | // Meta Test library for packages implementing SortedMap APIs 7 | 8 | // Random (non-repeating) sequence statically generated via https://www.random.org/sequences/ 9 | // Random (non-repeating) sequence dynamically generated via Knuth Shuffle 10 | 11 | import ( 12 | cryptoRand "crypto/rand" 13 | "errors" 14 | "fmt" 15 | "math/big" 16 | mathRand "math/rand/v2" 17 | "strconv" 18 | "testing" 19 | ) 20 | 21 | const ( 22 | testHugeNumKeys = 5000 23 | 24 | pseudoRandom = false 25 | pseudoRandomSeed1 = uint64(0) 26 | pseudoRandomSeed2 = uint64(0) 27 | ) 28 | 29 | var ( 30 | randSource *mathRand.Rand // A source for pseudo-random numbers (if selected) 31 | ) 32 | 33 | func metaTestAllButDeleteSimple(t *testing.T, tree SortedMap) { 34 | var ( 35 | err error 36 | found bool 37 | index int 38 | keyAsInt int 39 | keyAsKey Key 40 | numberOfItems int 41 | ok bool 42 | valueAsString string 43 | valueAsValue Value 44 | ) 45 | 46 | index, found, err = tree.BisectLeft(0) 47 | if err != nil { 48 | t.Fatal(err) 49 | } 50 | if index != -1 { 51 | t.Fatalf("BisectLeft(0).index of just initialized LLRB should have been -1... instead it was %v", index) 52 | } 53 | if found { 54 | t.Fatalf("BisectLeft(0).found of just initialized LLRB should have been false") 55 | } 56 | 57 | index, found, err = tree.BisectRight(0) 58 | if err != nil { 59 | t.Fatal(err) 60 | } 61 | if index != 0 { 62 | t.Fatalf("BisectRight(0).index of just initialized LLRB should have been 0... instead it was %v", index) 63 | } 64 | if found { 65 | t.Fatalf("BisectRight(0).found of just initialized LLRB should have been false") 66 | } 67 | 68 | _, _, ok, err = tree.GetByIndex(-1) 69 | if err != nil { 70 | t.Fatal(err) 71 | } 72 | if ok { 73 | t.Fatalf("GetByIndex(-1).ok should have been false") 74 | } 75 | 76 | _, _, ok, err = tree.GetByIndex(0) 77 | if err != nil { 78 | t.Fatal(err) 79 | } 80 | if ok { 81 | t.Fatalf("GetByIndex(0).ok [Case 1] of just initialized LLRB should have been false") 82 | } 83 | 84 | _, ok, err = tree.GetByKey(0) 85 | if err != nil { 86 | t.Fatal(err) 87 | } 88 | if ok { 89 | t.Fatalf("GetByKey(0).ok of just initialized LLRB should have been false") 90 | } 91 | 92 | numberOfItems, err = tree.Len() 93 | if err != nil { 94 | t.Fatal(err) 95 | } 96 | if numberOfItems != 0 { 97 | t.Fatalf("Len() [Case 1] of just initialized LLRB should have been 0... instead it was %v", numberOfItems) 98 | } 99 | 100 | ok, err = tree.Put(5, "5") 101 | if err != nil { 102 | t.Fatal(err) 103 | } 104 | if !ok { 105 | t.Fatalf("Put(5, \"5\").ok should have been true") 106 | } 107 | 108 | index, found, err = tree.BisectLeft(3) 109 | if err != nil { 110 | t.Fatal(err) 111 | } 112 | if index != -1 { 113 | t.Fatalf("BisectLeft(3).index [Case 1] should have been -1... instead it was %v", index) 114 | } 115 | if found { 116 | t.Fatalf("BisectLeft(3).found [Case 1] should have been false") 117 | } 118 | 119 | index, found, err = tree.BisectLeft(5) 120 | if err != nil { 121 | t.Fatal(err) 122 | } 123 | if index != 0 { 124 | t.Fatalf("BisectLeft(5).index [Case 1] should have been 0... instead it was %v", index) 125 | } 126 | if !found { 127 | t.Fatalf("BisectLeft(5).found [Case 1] should have been true") 128 | } 129 | 130 | index, found, err = tree.BisectLeft(7) 131 | if err != nil { 132 | t.Fatal(err) 133 | } 134 | if index != 0 { 135 | t.Fatalf("BisectLeft(7).index [Case 1] should have been 0... instead it was %v", index) 136 | } 137 | if found { 138 | t.Fatalf("BisectLeft(7).found [Case 1] should have been false") 139 | } 140 | 141 | index, found, err = tree.BisectRight(3) 142 | if err != nil { 143 | t.Fatal(err) 144 | } 145 | if index != 0 { 146 | t.Fatalf("BisectRight(3).index [Case 1] should have been 0... instead it was %v", index) 147 | } 148 | if found { 149 | t.Fatalf("BisectRight(3).found [Case 1] should have been false") 150 | } 151 | 152 | index, found, err = tree.BisectRight(5) 153 | if err != nil { 154 | t.Fatal(err) 155 | } 156 | if index != 0 { 157 | t.Fatalf("BisectRight(5).index [Case 1] should have been 0... instead it was %v", index) 158 | } 159 | if !found { 160 | t.Fatalf("BisectRight(5).found [Case 1] should have been true") 161 | } 162 | 163 | index, found, err = tree.BisectRight(7) 164 | if err != nil { 165 | t.Fatal(err) 166 | } 167 | if index != 1 { 168 | t.Fatalf("BisectRight(7).index [Case 1] should have been 1... instead it was %v", index) 169 | } 170 | if found { 171 | t.Fatalf("BisectRight(7).found [Case 1] should have been false") 172 | } 173 | 174 | keyAsKey, valueAsValue, ok, err = tree.GetByIndex(0) 175 | if err != nil { 176 | t.Fatal(err) 177 | } 178 | if !ok { 179 | t.Fatalf("GetByIndex(0).ok [Case 2] should have been true") 180 | } 181 | keyAsInt = keyAsKey.(int) 182 | if keyAsInt != 5 { 183 | t.Fatalf("GetByIndex(0).key [Case 2] should have been 5... instead it was %v", keyAsInt) 184 | } 185 | valueAsString = valueAsValue.(string) 186 | if valueAsString != "5" { 187 | t.Fatalf("GetByIndex(0).value [Case 2] should have been \"5\"... instead it was \"%v\"", valueAsString) 188 | } 189 | 190 | _, _, ok, err = tree.GetByIndex(1) 191 | if err != nil { 192 | t.Fatal(err) 193 | } 194 | if ok { 195 | t.Fatalf("GetByIndex(1).ok [Case 1] should have been false") 196 | } 197 | 198 | _, ok, err = tree.GetByKey(3) 199 | if err != nil { 200 | t.Fatal(err) 201 | } 202 | if ok { 203 | t.Fatalf("GetByKey(3).ok [Case 1] should have been false") 204 | } 205 | 206 | valueAsValue, ok, err = tree.GetByKey(5) 207 | if err != nil { 208 | t.Fatal(err) 209 | } 210 | if !ok { 211 | t.Fatalf("GetByKey(5).ok [Case 1] should have been true") 212 | } 213 | valueAsString = valueAsValue.(string) 214 | if valueAsString != "5" { 215 | t.Fatalf("GetByKey(5).value [Case 1] should have been \"5\"... instead it was \"%v\"", valueAsString) 216 | } 217 | 218 | _, ok, err = tree.GetByKey(7) 219 | if err != nil { 220 | t.Fatal(err) 221 | } 222 | if ok { 223 | t.Fatalf("GetByKey(7).ok [Case 1] should have been false") 224 | } 225 | 226 | numberOfItems, err = tree.Len() 227 | if err != nil { 228 | t.Fatal(err) 229 | } 230 | if numberOfItems != 1 { 231 | t.Fatalf("Len() [Case 2] should have been 1... instead it was %v", numberOfItems) 232 | } 233 | 234 | ok, err = tree.Put(3, "3") 235 | if err != nil { 236 | t.Fatal(err) 237 | } 238 | if !ok { 239 | t.Fatalf("Put(3, \"3\").ok should have been true") 240 | } 241 | 242 | ok, err = tree.Put(7, "7") 243 | if err != nil { 244 | t.Fatal(err) 245 | } 246 | if !ok { 247 | t.Fatalf("Put(7, \"7\").ok should have been true") 248 | } 249 | 250 | index, found, err = tree.BisectLeft(2) 251 | if err != nil { 252 | t.Fatal(err) 253 | } 254 | if index != -1 { 255 | t.Fatalf("BisectLeft(2).index should have been -1... instead it was %v", index) 256 | } 257 | if found { 258 | t.Fatalf("BisectLeft(2).found should have been false") 259 | } 260 | 261 | index, found, err = tree.BisectLeft(3) 262 | if err != nil { 263 | t.Fatal(err) 264 | } 265 | if index != 0 { 266 | t.Fatalf("BisectLeft(3).index [Case 2] should have been 0... instead it was %v", index) 267 | } 268 | if !found { 269 | t.Fatalf("BisectLeft(3).found [Case 2] should have been true") 270 | } 271 | 272 | index, found, err = tree.BisectLeft(4) 273 | if err != nil { 274 | t.Fatal(err) 275 | } 276 | if index != 0 { 277 | t.Fatalf("BisectLeft(4).index should have been 0... instead it was %v", index) 278 | } 279 | if found { 280 | t.Fatalf("BisectLeft(4).found should have been false") 281 | } 282 | 283 | index, found, err = tree.BisectLeft(5) 284 | if err != nil { 285 | t.Fatal(err) 286 | } 287 | if index != 1 { 288 | t.Fatalf("BisectLeft(5).index [Case 2] should have been 1... instead it was %v", index) 289 | } 290 | if !found { 291 | t.Fatalf("BisectLeft(5).found [Case 2] should have been true") 292 | } 293 | 294 | index, found, err = tree.BisectLeft(6) 295 | if err != nil { 296 | t.Fatal(err) 297 | } 298 | if index != 1 { 299 | t.Fatalf("BisectLeft(5).index should have been 1... instead it was %v", index) 300 | } 301 | if found { 302 | t.Fatalf("BisectLeft(6).found should have been false") 303 | } 304 | 305 | index, found, err = tree.BisectLeft(7) 306 | if err != nil { 307 | t.Fatal(err) 308 | } 309 | if index != 2 { 310 | t.Fatalf("BisectLeft(7).index [Case 2] should have been 2... instead it was %v", index) 311 | } 312 | if !found { 313 | t.Fatalf("BisectLeft(7).found [Case 2] should have been true") 314 | } 315 | 316 | index, found, err = tree.BisectLeft(8) 317 | if err != nil { 318 | t.Fatal(err) 319 | } 320 | if index != 2 { 321 | t.Fatalf("BisectLeft(8).index should have been 2... instead it was %v", index) 322 | } 323 | if found { 324 | t.Fatalf("BisectLeft(8).found should have been false") 325 | } 326 | 327 | index, found, err = tree.BisectRight(2) 328 | if err != nil { 329 | t.Fatal(err) 330 | } 331 | if index != 0 { 332 | t.Fatalf("BisectRight(2).index should have been 0... instead it was %v", index) 333 | } 334 | if found { 335 | t.Fatalf("BisectRight(2).found should have been false") 336 | } 337 | 338 | index, found, err = tree.BisectRight(3) 339 | if err != nil { 340 | t.Fatal(err) 341 | } 342 | if index != 0 { 343 | t.Fatalf("BisectRight(3).index [Case 2] should have been 0... instead it was %v", index) 344 | } 345 | if !found { 346 | t.Fatalf("BisectRight(3).found [Case 2] should have been true") 347 | } 348 | 349 | index, found, err = tree.BisectRight(4) 350 | if err != nil { 351 | t.Fatal(err) 352 | } 353 | if index != 1 { 354 | t.Fatalf("BisectRight(4).index should have been 1... instead it was %v", index) 355 | } 356 | if found { 357 | t.Fatalf("BisectRight(4).found should have been false") 358 | } 359 | 360 | index, found, err = tree.BisectRight(5) 361 | if err != nil { 362 | t.Fatal(err) 363 | } 364 | if index != 1 { 365 | t.Fatalf("BisectRight(5).index [Case 2] should have been 1... instead it was %v", index) 366 | } 367 | if !found { 368 | t.Fatalf("BisectRight(5).found [Case 2] should have been true") 369 | } 370 | 371 | index, found, err = tree.BisectRight(6) 372 | if err != nil { 373 | t.Fatal(err) 374 | } 375 | if index != 2 { 376 | t.Fatalf("BisectRight(5).index should have been 2... instead it was %v", index) 377 | } 378 | if found { 379 | t.Fatalf("BisectRight(6).found should have been false") 380 | } 381 | 382 | index, found, err = tree.BisectRight(7) 383 | if err != nil { 384 | t.Fatal(err) 385 | } 386 | if index != 2 { 387 | t.Fatalf("BisectRight(7).index [Case 2] should have been 2... instead it was %v", index) 388 | } 389 | if !found { 390 | t.Fatalf("BisectRight(7).found [Case 2] should have been true") 391 | } 392 | 393 | index, found, err = tree.BisectRight(8) 394 | if err != nil { 395 | t.Fatal(err) 396 | } 397 | if index != 3 { 398 | t.Fatalf("BisectRight(8).index should have been 3... instead it was %v", index) 399 | } 400 | if found { 401 | t.Fatalf("BisectRight(8).found should have been false") 402 | } 403 | 404 | keyAsKey, valueAsValue, ok, err = tree.GetByIndex(0) 405 | if err != nil { 406 | t.Fatal(err) 407 | } 408 | if !ok { 409 | t.Fatalf("GetByIndex(0).ok [Case 3] should have been true") 410 | } 411 | keyAsInt = keyAsKey.(int) 412 | if keyAsInt != 3 { 413 | t.Fatalf("GetByIndex(0).key [Case 3] should have been 3... instead it was %v", keyAsInt) 414 | } 415 | valueAsString = valueAsValue.(string) 416 | if valueAsString != "3" { 417 | t.Fatalf("GetByIndex(0).value [Case 3] should have been \"3\"... instead it was \"%v\"", valueAsString) 418 | } 419 | 420 | keyAsKey, valueAsValue, ok, err = tree.GetByIndex(1) 421 | if err != nil { 422 | t.Fatal(err) 423 | } 424 | if !ok { 425 | t.Fatalf("GetByIndex(1).ok [Case 2] should have been true") 426 | } 427 | keyAsInt = keyAsKey.(int) 428 | if keyAsInt != 5 { 429 | t.Fatalf("GetByIndex(1).key [Case 2] should have been 5... instead it was %v", keyAsInt) 430 | } 431 | valueAsString = valueAsValue.(string) 432 | if valueAsString != "5" { 433 | t.Fatalf("GetByIndex(1).value [Case 2] should have been \"5\"... instead it was \"%v\"", valueAsString) 434 | } 435 | 436 | keyAsKey, valueAsValue, ok, err = tree.GetByIndex(2) 437 | if err != nil { 438 | t.Fatal(err) 439 | } 440 | if !ok { 441 | t.Fatalf("GetByIndex(2).ok [Case 1] should have been true") 442 | } 443 | keyAsInt = keyAsKey.(int) 444 | if keyAsInt != 7 { 445 | t.Fatalf("GetByIndex(2).key [Case 1] should have been 7... instead it was %v", keyAsInt) 446 | } 447 | valueAsString = valueAsValue.(string) 448 | if valueAsString != "7" { 449 | t.Fatalf("GetByIndex(2).value [Case 1] should have been \"7\"... instead it was \"%v\"", valueAsString) 450 | } 451 | 452 | _, _, ok, err = tree.GetByIndex(3) 453 | if err != nil { 454 | t.Fatal(err) 455 | } 456 | if ok { 457 | t.Fatalf("GetByIndex(3).ok should have been false") 458 | } 459 | 460 | _, ok, err = tree.GetByKey(2) 461 | if err != nil { 462 | t.Fatal(err) 463 | } 464 | if ok { 465 | t.Fatalf("GetByKey(2).ok should have been false") 466 | } 467 | 468 | valueAsValue, ok, err = tree.GetByKey(3) 469 | if err != nil { 470 | t.Fatal(err) 471 | } 472 | if !ok { 473 | t.Fatalf("GetByKey(3).ok [Case 2] should have been true") 474 | } 475 | valueAsString = valueAsValue.(string) 476 | if valueAsString != "3" { 477 | t.Fatalf("GetByKey(3).value [Case 2] should have been \"3\"... instead it was \"%v\"", valueAsString) 478 | } 479 | 480 | _, ok, err = tree.GetByKey(4) 481 | if err != nil { 482 | t.Fatal(err) 483 | } 484 | if ok { 485 | t.Fatalf("GetByKey(4).ok should have been false") 486 | } 487 | 488 | valueAsValue, ok, err = tree.GetByKey(5) 489 | if err != nil { 490 | t.Fatal(err) 491 | } 492 | if !ok { 493 | t.Fatalf("GetByKey(5).ok [Case 2] should have been true") 494 | } 495 | valueAsString = valueAsValue.(string) 496 | if valueAsString != "5" { 497 | t.Fatalf("GetByKey(5).value [Case 2] should have been \"5\"... instead it was \"%v\"", valueAsString) 498 | } 499 | 500 | _, ok, err = tree.GetByKey(6) 501 | if err != nil { 502 | t.Fatal(err) 503 | } 504 | if ok { 505 | t.Fatalf("GetByKey(6).ok should have been false") 506 | } 507 | 508 | valueAsValue, ok, err = tree.GetByKey(7) 509 | if err != nil { 510 | t.Fatal(err) 511 | } 512 | if !ok { 513 | t.Fatalf("GetByKey(7).ok [Case 2] should have been true") 514 | } 515 | valueAsString = valueAsValue.(string) 516 | if valueAsString != "7" { 517 | t.Fatalf("GetByKey(7).value [Case 2] should have been \"7\"... instead it was \"%v\"", valueAsString) 518 | } 519 | 520 | _, ok, err = tree.GetByKey(8) 521 | if err != nil { 522 | t.Fatal(err) 523 | } 524 | if ok { 525 | t.Fatalf("GetByKey(8).ok should have been false") 526 | } 527 | 528 | numberOfItems, err = tree.Len() 529 | if err != nil { 530 | t.Fatal(err) 531 | } 532 | if numberOfItems != 3 { 533 | t.Fatalf("Len() [Case 3] should have been 3... instead it was %v", numberOfItems) 534 | } 535 | 536 | ok, err = tree.PatchByIndex(-1, "") 537 | if err != nil { 538 | t.Fatal(err) 539 | } 540 | if ok { 541 | t.Fatalf("PatchByIndex(-1, \"\").ok should have been false") 542 | } 543 | 544 | ok, err = tree.PatchByIndex(3, "") 545 | if err != nil { 546 | t.Fatal(err) 547 | } 548 | if ok { 549 | t.Fatalf("PatchByIndex(3, \"\").ok should have been false") 550 | } 551 | 552 | ok, err = tree.PatchByKey(1, "") 553 | if err != nil { 554 | t.Fatal(err) 555 | } 556 | if ok { 557 | t.Fatalf("PatchByKey(1, \"\").ok should have been false") 558 | } 559 | 560 | ok, err = tree.PatchByIndex(0, "T") 561 | if err != nil { 562 | t.Fatal(err) 563 | } 564 | if !ok { 565 | t.Fatalf("PatchByIndex(0, \"T\").ok should have been true") 566 | } 567 | 568 | valueAsValue, ok, err = tree.GetByKey(3) 569 | if err != nil { 570 | t.Fatal(err) 571 | } 572 | if !ok { 573 | t.Fatalf("GetByKey(3).ok [Case 3] should have been true") 574 | } 575 | valueAsString = valueAsValue.(string) 576 | if valueAsString != "T" { 577 | t.Fatalf("GetByKey(3).value [Case 3] should have been \"3\"... instead it was \"%v\"", valueAsString) 578 | } 579 | 580 | ok, err = tree.PatchByKey(7, "S") 581 | if err != nil { 582 | t.Fatal(err) 583 | } 584 | if !ok { 585 | t.Fatalf("PatchByKey(7, \"S\").ok should have been true") 586 | } 587 | 588 | keyAsKey, valueAsValue, ok, err = tree.GetByIndex(2) 589 | if err != nil { 590 | t.Fatal(err) 591 | } 592 | if !ok { 593 | t.Fatalf("GetByIndex(2).ok [Case 2] should have been true") 594 | } 595 | keyAsInt = keyAsKey.(int) 596 | if keyAsInt != 7 { 597 | t.Fatalf("GetByIndex(2).key [Case 2] should have been 7... instead it was %v", keyAsInt) 598 | } 599 | valueAsString = valueAsValue.(string) 600 | if valueAsString != "S" { 601 | t.Fatalf("GetByIndex(2).value [Case 2] should have been \"S\"... instead it was \"%v\"", valueAsString) 602 | } 603 | } 604 | 605 | func metaTestDeleteByIndexSimple(t *testing.T, tree SortedMap) { 606 | var ( 607 | err error 608 | numberOfItems int 609 | ok bool 610 | ) 611 | 612 | ok, err = tree.Put(3, "3") 613 | if err != nil { 614 | t.Fatal(err) 615 | } 616 | if !ok { 617 | t.Fatalf("Put(3, \"3\").ok should have been true") 618 | } 619 | 620 | ok, err = tree.Put(5, "5") 621 | if err != nil { 622 | t.Fatal(err) 623 | } 624 | if !ok { 625 | t.Fatalf("Put(5, \"5\").ok should have been true") 626 | } 627 | 628 | ok, err = tree.Put(7, "7") 629 | if err != nil { 630 | t.Fatal(err) 631 | } 632 | if !ok { 633 | t.Fatalf("Put(7, \"7\").ok should have been true") 634 | } 635 | 636 | numberOfItems, err = tree.Len() 637 | if err != nil { 638 | t.Fatal(err) 639 | } 640 | if numberOfItems != 3 { 641 | t.Fatalf("Len() [Case 1] should have been 3... instead it was %v", numberOfItems) 642 | } 643 | 644 | ok, err = tree.DeleteByIndex(-1) 645 | if err != nil { 646 | t.Fatal(err) 647 | } 648 | if ok { 649 | t.Fatalf("DeleteByIndex(-1).ok should have been false") 650 | } 651 | 652 | ok, err = tree.DeleteByIndex(3) 653 | if err != nil { 654 | t.Fatal(err) 655 | } 656 | if ok { 657 | t.Fatalf("DeleteByIndex(3).ok should have been false") 658 | } 659 | 660 | ok, err = tree.DeleteByIndex(2) 661 | if err != nil { 662 | t.Fatal(err) 663 | } 664 | if !ok { 665 | t.Fatalf("DeleteByIndex(2).ok should have been true") 666 | } 667 | 668 | ok, err = tree.DeleteByIndex(0) 669 | if err != nil { 670 | t.Fatal(err) 671 | } 672 | if !ok { 673 | t.Fatalf("DeleteByIndex(0).ok [Case 1] should have been true") 674 | } 675 | 676 | ok, err = tree.DeleteByIndex(0) 677 | if err != nil { 678 | t.Fatal(err) 679 | } 680 | if !ok { 681 | t.Fatalf("DeleteByIndex(0).ok [Case 2] should have been true") 682 | } 683 | 684 | numberOfItems, err = tree.Len() 685 | if err != nil { 686 | t.Fatal(err) 687 | } 688 | if numberOfItems != 0 { 689 | t.Fatalf("Len() [Case 2] should have been 0... instead it was %v", numberOfItems) 690 | } 691 | } 692 | 693 | func metaTestDeleteByKeySimple(t *testing.T, tree SortedMap) { 694 | var ( 695 | err error 696 | numberOfItems int 697 | ok bool 698 | ) 699 | 700 | ok, err = tree.Put(3, "3") 701 | if err != nil { 702 | t.Fatal(err) 703 | } 704 | if !ok { 705 | t.Fatalf("Put(3, \"3\").ok should have been true") 706 | } 707 | 708 | ok, err = tree.Put(5, "5") 709 | if err != nil { 710 | t.Fatal(err) 711 | } 712 | if !ok { 713 | t.Fatalf("Put(5, \"5\").ok should have been true") 714 | } 715 | 716 | ok, err = tree.Put(7, "7") 717 | if err != nil { 718 | t.Fatal(err) 719 | } 720 | if !ok { 721 | t.Fatalf("Put(7, \"7\").ok should have been true") 722 | } 723 | 724 | numberOfItems, err = tree.Len() 725 | if err != nil { 726 | t.Fatal(err) 727 | } 728 | if numberOfItems != 3 { 729 | t.Fatalf("Len() [Case 1] should have been 3... instead it was %v", numberOfItems) 730 | } 731 | 732 | ok, err = tree.DeleteByKey(2) 733 | if err != nil { 734 | t.Fatal(err) 735 | } 736 | if ok { 737 | t.Fatalf("DeleteByKey(2).ok should have been false") 738 | } 739 | 740 | ok, err = tree.DeleteByKey(3) 741 | if err != nil { 742 | t.Fatal(err) 743 | } 744 | if !ok { 745 | t.Fatalf("DeleteByKey(3).ok should have been true") 746 | } 747 | 748 | ok, err = tree.DeleteByKey(4) 749 | if err != nil { 750 | t.Fatal(err) 751 | } 752 | if ok { 753 | t.Fatalf("DeleteByKey(4).ok should have been false") 754 | } 755 | 756 | ok, err = tree.DeleteByKey(5) 757 | if err != nil { 758 | t.Fatal(err) 759 | } 760 | if !ok { 761 | t.Fatalf("DeleteByKey(5).ok should have been true") 762 | } 763 | 764 | ok, err = tree.DeleteByKey(6) 765 | if err != nil { 766 | t.Fatal(err) 767 | } 768 | if ok { 769 | t.Fatalf("DeleteByKey(6).ok should have been false") 770 | } 771 | 772 | ok, err = tree.DeleteByKey(7) 773 | if err != nil { 774 | t.Fatal(err) 775 | } 776 | if !ok { 777 | t.Fatalf("DeleteByKey(7).ok should have been true") 778 | } 779 | 780 | ok, err = tree.DeleteByKey(8) 781 | if err != nil { 782 | t.Fatal(err) 783 | } 784 | if ok { 785 | t.Fatalf("DeleteByKey(8).ok should have been false") 786 | } 787 | 788 | numberOfItems, err = tree.Len() 789 | if err != nil { 790 | t.Fatal(err) 791 | } 792 | if numberOfItems != 0 { 793 | t.Fatalf("Len() [Case 2] should have been 0... instead it was %v", numberOfItems) 794 | } 795 | } 796 | 797 | func testKnuthShuffledIntSlice(n int) (intSlice []int, err error) { 798 | var ( 799 | swapFrom int64 800 | swapTo int64 801 | ) 802 | intSlice = make([]int, n) 803 | for i := range n { 804 | intSlice[i] = i 805 | } 806 | for swapFrom = int64(n - 1); swapFrom > int64(0); swapFrom-- { 807 | if pseudoRandom { 808 | if randSource == nil { 809 | randSource = mathRand.New(mathRand.NewPCG(pseudoRandomSeed1, pseudoRandomSeed2)) 810 | } 811 | 812 | swapTo = randSource.Int64N(swapFrom + 1) 813 | } else { 814 | swapFromPlusOneBigIntPtr := big.NewInt(swapFrom + 1) 815 | 816 | swapToBigIntPtr, nonShadowingErr := cryptoRand.Int(cryptoRand.Reader, swapFromPlusOneBigIntPtr) 817 | if nonShadowingErr != nil { 818 | err = fmt.Errorf("cryptoRand.Int(cryptoRand.Reader, swapFromPlusOneBigIntPtr) returned error == \"%v\"", nonShadowingErr) 819 | return 820 | } 821 | 822 | swapTo = swapToBigIntPtr.Int64() 823 | } 824 | 825 | if swapFrom != swapTo { 826 | intSlice[swapFrom], intSlice[swapTo] = intSlice[swapTo], intSlice[swapFrom] 827 | } 828 | } 829 | 830 | err = nil 831 | return 832 | } 833 | 834 | func testFetchIndicesToDeleteNormalized(indicesToDeleteNotNormalized []int) (indicesToDeleteNormalized []int) { 835 | var ( 836 | elementIndexInner int 837 | elementIndexOuter int 838 | elementValueInner int 839 | elementValueOuter int 840 | ) 841 | 842 | indicesToDeleteNormalized = make([]int, len(indicesToDeleteNotNormalized)) 843 | 844 | for elementIndexOuter, elementValueOuter = range indicesToDeleteNotNormalized { 845 | indicesToDeleteNormalized[elementIndexOuter] = elementValueOuter 846 | } 847 | 848 | for elementIndexOuter, elementValueOuter = range indicesToDeleteNormalized { 849 | for elementIndexInner = (elementIndexOuter + 1); elementIndexInner < len(indicesToDeleteNormalized); elementIndexInner++ { 850 | elementValueInner = indicesToDeleteNormalized[elementIndexInner] 851 | if elementValueInner > elementValueOuter { 852 | elementValueInner-- 853 | } 854 | indicesToDeleteNormalized[elementIndexInner] = elementValueInner 855 | } 856 | } 857 | 858 | return 859 | } 860 | 861 | func testInsertGetDeleteByIndex(t *testing.T, tree SortedMap, keysToInsert, indicesToGet, indicesToDeleteNotNormalized []int) { 862 | var ( 863 | err error 864 | indexToDelete int 865 | indexToGet int 866 | indicesToDeleteNormalized []int 867 | keyAsInt int 868 | keyAsKey Key 869 | keyToInsert int 870 | numberOfItems int 871 | ok bool 872 | valueAsString string 873 | valueAsValue Value 874 | ) 875 | 876 | numberOfItems, err = tree.Len() 877 | if err != nil { 878 | t.Fatal(err) 879 | } 880 | if numberOfItems != 0 { 881 | t.Fatalf("Len() [Case 1] should have been 0... instead it was %v", numberOfItems) 882 | } 883 | 884 | for _, keyToInsert = range keysToInsert { 885 | valueAsString = strconv.Itoa(keyToInsert) 886 | ok, err = tree.Put(keyToInsert, valueAsString) 887 | if err != nil { 888 | t.Fatal(err) 889 | } 890 | if !ok { 891 | t.Fatalf("Put(%v, \"%v\").ok should have been true", keyToInsert, valueAsString) 892 | } 893 | } 894 | 895 | numberOfItems, err = tree.Len() 896 | if err != nil { 897 | t.Fatal(err) 898 | } 899 | if len(keysToInsert) != numberOfItems { 900 | t.Fatalf("Len() [Case 2] should have been %v... instead it was %v", len(keysToInsert), numberOfItems) 901 | } 902 | 903 | for _, indexToGet = range indicesToGet { 904 | keyAsKey, valueAsValue, ok, err = tree.GetByIndex(indexToGet) 905 | if err != nil { 906 | t.Fatal(err) 907 | } 908 | if !ok { 909 | t.Fatalf("GetByIndex(%v).ok should have been true", indexToGet) 910 | } 911 | keyAsInt = keyAsKey.(int) 912 | if indexToGet != keyAsInt { 913 | t.Fatalf("GetByIndex(%v).key should have been %v... instead it was %v", indexToGet, indexToGet, keyAsInt) 914 | } 915 | valueAsString = valueAsValue.(string) 916 | if strconv.Itoa(indexToGet) != valueAsString { 917 | t.Fatalf("GetByIndex(%v).value should have been \"%v\"... instead it was \"%v\"", indexToGet, strconv.Itoa(indexToGet), valueAsString) 918 | } 919 | } 920 | 921 | indicesToDeleteNormalized = testFetchIndicesToDeleteNormalized(indicesToDeleteNotNormalized) 922 | 923 | for _, indexToDelete = range indicesToDeleteNormalized { 924 | ok, err = tree.DeleteByIndex(indexToDelete) 925 | if err != nil { 926 | t.Fatal(err) 927 | } 928 | if !ok { 929 | t.Fatalf("DeleteByIndex(%v).ok should have been true", indexToDelete) 930 | } 931 | } 932 | 933 | numberOfItems, err = tree.Len() 934 | if err != nil { 935 | t.Fatal(err) 936 | } 937 | if numberOfItems != 0 { 938 | t.Fatalf("Len() [Case 3] should have been %v... instead it was %v", 0, numberOfItems) 939 | } 940 | } 941 | 942 | func metaTestInsertGetDeleteByIndexTrivial(t *testing.T, tree SortedMap) { 943 | keysToInsert := []int{ 944 | 5, 3, 2, 6, 1, 0, 4, 945 | } 946 | 947 | indicesToGet := []int{ 948 | 0, 5, 6, 1, 2, 4, 3, 949 | } 950 | 951 | indicesToDeleteNotNormalized := []int{ 952 | 3, 2, 5, 4, 0, 6, 1, 953 | } 954 | 955 | testInsertGetDeleteByIndex(t, tree, keysToInsert, indicesToGet, indicesToDeleteNotNormalized) 956 | } 957 | 958 | func metaTestInsertGetDeleteByIndexSmall(t *testing.T, tree SortedMap) { 959 | keysToInsert := []int{ 960 | 2, 0, 5, 31, 1, 12, 30, 13, 21, 37, 17, 22, 24, 11, 35, 6, 36, 15, 23, 19, 961 | 20, 25, 3, 34, 14, 27, 26, 33, 28, 9, 29, 16, 18, 38, 7, 39, 8, 32, 4, 10, 962 | } 963 | 964 | indicesToGet := []int{ 965 | 7, 11, 8, 33, 4, 21, 13, 0, 10, 39, 29, 35, 23, 3, 30, 24, 9, 5, 37, 38, 966 | 17, 14, 25, 1, 16, 36, 12, 31, 2, 27, 19, 15, 6, 20, 22, 18, 34, 28, 26, 32, 967 | } 968 | 969 | indicesToDeleteNotNormalized := []int{ 970 | 8, 18, 24, 12, 6, 28, 34, 25, 29, 38, 13, 9, 32, 7, 26, 39, 17, 37, 2, 3, 971 | 4, 23, 36, 22, 15, 19, 20, 11, 0, 5, 35, 33, 14, 27, 1, 10, 30, 31, 21, 16, 972 | } 973 | 974 | testInsertGetDeleteByIndex(t, tree, keysToInsert, indicesToGet, indicesToDeleteNotNormalized) 975 | } 976 | 977 | func metaTestInsertGetDeleteByIndexLarge(t *testing.T, tree SortedMap) { 978 | keysToInsert := []int{ 979 | 940, 565, 961, 599, 254, 252, 170, 812, 804, 334, 449, 131, 527, 44, 236, 636, 233, 318, 299, 725, 980 | 4, 960, 721, 281, 720, 975, 811, 990, 964, 324, 286, 301, 474, 803, 65, 918, 967, 905, 794, 649, 981 | 935, 560, 261, 306, 980, 797, 680, 309, 788, 62, 135, 845, 229, 581, 57, 459, 687, 361, 477, 968, 982 | 987, 87, 985, 513, 351, 313, 944, 90, 429, 832, 942, 320, 377, 260, 13, 718, 900, 629, 924, 153, 983 | 709, 762, 146, 280, 856, 776, 768, 814, 765, 285, 852, 568, 686, 630, 752, 144, 611, 15, 174, 160, 984 | 375, 37, 618, 770, 657, 147, 829, 781, 743, 899, 86, 843, 866, 64, 192, 128, 914, 534, 659, 16, 985 | 579, 199, 733, 840, 507, 430, 826, 8, 875, 278, 621, 1, 880, 665, 766, 18, 610, 295, 31, 288, 986 | 374, 484, 575, 978, 427, 416, 300, 129, 878, 719, 350, 561, 792, 750, 113, 34, 825, 773, 779, 7, 987 | 55, 80, 958, 970, 60, 494, 929, 727, 78, 983, 902, 103, 437, 139, 972, 933, 909, 248, 913, 185, 988 | 570, 392, 923, 412, 137, 263, 404, 609, 438, 869, 319, 317, 329, 461, 421, 655, 114, 257, 119, 966, 989 | 884, 653, 988, 849, 602, 120, 69, 436, 259, 572, 274, 145, 576, 21, 385, 669, 273, 995, 830, 425, 990 | 562, 731, 388, 724, 470, 540, 97, 231, 623, 100, 457, 934, 608, 154, 965, 672, 251, 772, 499, 323, 991 | 264, 27, 379, 469, 888, 433, 885, 30, 305, 115, 212, 284, 696, 426, 580, 698, 168, 332, 746, 410, 992 | 557, 276, 569, 466, 167, 614, 481, 590, 244, 166, 716, 14, 79, 904, 642, 783, 982, 230, 704, 454, 993 | 0, 444, 920, 321, 3, 539, 993, 39, 683, 771, 181, 693, 328, 999, 628, 127, 930, 956, 367, 232, 994 | 805, 938, 639, 705, 894, 310, 83, 269, 148, 515, 640, 521, 183, 163, 643, 862, 741, 778, 478, 304, 995 | 708, 511, 460, 303, 889, 739, 132, 53, 46, 986, 732, 344, 650, 592, 542, 312, 641, 519, 895, 745, 996 | 679, 777, 979, 827, 336, 43, 180, 222, 109, 991, 343, 859, 819, 887, 423, 453, 19, 818, 806, 451, 997 | 890, 963, 906, 722, 165, 813, 627, 420, 339, 916, 749, 910, 355, 652, 85, 465, 266, 33, 567, 200, 998 | 925, 279, 842, 104, 530, 728, 456, 325, 577, 327, 685, 997, 677, 759, 387, 712, 88, 348, 441, 292, 999 | 322, 293, 815, 178, 555, 214, 563, 213, 59, 807, 337, 715, 791, 376, 399, 977, 954, 586, 197, 510, 1000 | 822, 648, 863, 49, 881, 908, 883, 287, 210, 853, 668, 391, 864, 703, 193, 514, 91, 681, 156, 901, 1001 | 699, 891, 52, 54, 836, 431, 512, 143, 398, 941, 371, 442, 690, 76, 545, 389, 723, 736, 989, 225, 1002 | 971, 841, 205, 701, 82, 345, 400, 331, 571, 142, 241, 443, 882, 717, 11, 600, 613, 40, 757, 589, 1003 | 138, 250, 211, 238, 29, 247, 203, 860, 36, 440, 202, 501, 258, 175, 714, 94, 227, 676, 786, 634, 1004 | 742, 624, 896, 450, 932, 769, 61, 622, 937, 480, 617, 347, 338, 158, 631, 684, 422, 9, 96, 748, 1005 | 546, 607, 141, 184, 223, 38, 190, 378, 226, 81, 730, 173, 10, 744, 73, 664, 820, 475, 795, 502, 1006 | 529, 547, 948, 637, 667, 262, 333, 992, 26, 538, 601, 99, 463, 408, 848, 403, 159, 697, 472, 417, 1007 | 738, 177, 296, 493, 12, 726, 503, 780, 646, 50, 912, 671, 228, 939, 504, 349, 108, 591, 585, 357, 1008 | 314, 854, 487, 130, 596, 734, 949, 121, 785, 936, 823, 877, 625, 283, 204, 962, 419, 467, 928, 390, 1009 | 124, 871, 215, 747, 520, 48, 663, 352, 25, 66, 692, 584, 382, 548, 846, 75, 63, 23, 188, 981, 1010 | 445, 136, 243, 41, 176, 651, 833, 868, 72, 945, 411, 239, 994, 439, 710, 356, 342, 255, 372, 106, 1011 | 386, 473, 162, 974, 381, 898, 110, 767, 735, 536, 315, 605, 761, 140, 362, 353, 216, 809, 816, 660, 1012 | 409, 240, 267, 689, 751, 620, 221, 455, 335, 509, 360, 151, 518, 298, 277, 47, 952, 208, 393, 218, 1013 | 756, 817, 101, 532, 462, 774, 973, 265, 224, 838, 206, 543, 24, 92, 93, 855, 354, 753, 597, 666, 1014 | 675, 186, 775, 587, 415, 688, 531, 464, 297, 58, 535, 976, 525, 397, 492, 850, 67, 405, 793, 541, 1015 | 32, 424, 302, 369, 234, 824, 656, 754, 593, 573, 98, 702, 879, 340, 922, 865, 713, 662, 330, 559, 1016 | 615, 858, 808, 122, 695, 380, 524, 152, 790, 249, 432, 700, 414, 553, 915, 556, 800, 483, 446, 272, 1017 | 291, 674, 164, 682, 220, 196, 897, 35, 758, 516, 566, 544, 588, 635, 552, 760, 290, 947, 707, 373, 1018 | 51, 471, 632, 346, 931, 517, 207, 407, 943, 45, 755, 706, 893, 506, 729, 533, 645, 953, 522, 491, 1019 | 550, 447, 644, 633, 235, 498, 801, 594, 112, 654, 268, 201, 316, 20, 921, 458, 435, 270, 256, 911, 1020 | 564, 496, 134, 107, 927, 311, 876, 526, 294, 917, 448, 56, 957, 574, 523, 359, 799, 366, 187, 802, 1021 | 505, 959, 551, 17, 150, 182, 434, 428, 401, 647, 837, 583, 42, 737, 670, 161, 396, 497, 857, 870, 1022 | 839, 595, 661, 89, 84, 764, 341, 984, 831, 603, 70, 370, 358, 275, 489, 172, 626, 246, 155, 395, 1023 | 368, 892, 872, 95, 485, 179, 969, 782, 998, 479, 384, 116, 326, 5, 71, 22, 919, 694, 907, 612, 1024 | 606, 289, 365, 406, 691, 282, 74, 673, 476, 763, 946, 798, 996, 117, 951, 488, 740, 133, 242, 619, 1025 | 191, 711, 578, 363, 528, 784, 217, 508, 171, 834, 198, 787, 413, 537, 638, 482, 102, 126, 6, 194, 1026 | 271, 678, 549, 245, 950, 598, 926, 402, 418, 495, 28, 658, 861, 835, 237, 123, 307, 554, 873, 490, 1027 | 2, 558, 383, 219, 500, 105, 195, 886, 157, 903, 169, 468, 68, 821, 810, 308, 452, 847, 582, 867, 1028 | 851, 844, 789, 111, 955, 125, 118, 874, 364, 616, 209, 149, 77, 828, 604, 189, 796, 253, 486, 394, 1029 | } 1030 | 1031 | indicesToGet := []int{ 1032 | 92, 628, 523, 154, 46, 111, 212, 385, 342, 705, 265, 543, 281, 999, 951, 307, 565, 252, 638, 352, 1033 | 632, 956, 408, 128, 806, 177, 75, 924, 832, 76, 562, 131, 755, 214, 453, 159, 635, 709, 618, 164, 1034 | 218, 791, 340, 260, 53, 667, 216, 639, 837, 387, 483, 456, 681, 993, 897, 583, 189, 446, 173, 93, 1035 | 361, 417, 463, 469, 2, 642, 511, 190, 809, 659, 754, 70, 636, 490, 81, 441, 354, 979, 871, 333, 1036 | 894, 650, 158, 627, 866, 458, 816, 226, 519, 674, 95, 557, 822, 608, 570, 394, 78, 572, 16, 774, 1037 | 836, 163, 435, 671, 347, 309, 982, 367, 505, 730, 239, 887, 231, 320, 963, 646, 554, 694, 316, 884, 1038 | 867, 606, 677, 213, 6, 914, 663, 852, 749, 311, 73, 559, 66, 700, 990, 985, 911, 548, 360, 957, 1039 | 869, 449, 953, 601, 733, 358, 949, 403, 895, 788, 821, 273, 7, 117, 874, 912, 568, 855, 919, 404, 1040 | 624, 448, 878, 308, 248, 192, 541, 233, 732, 29, 467, 528, 889, 533, 680, 362, 455, 988, 372, 506, 1041 | 958, 556, 936, 170, 811, 729, 687, 222, 844, 351, 699, 860, 492, 20, 517, 977, 59, 413, 862, 376, 1042 | 179, 301, 465, 113, 199, 580, 713, 474, 928, 607, 366, 771, 918, 865, 551, 598, 643, 161, 904, 292, 1043 | 906, 972, 703, 101, 939, 997, 955, 317, 145, 420, 858, 85, 187, 484, 609, 740, 279, 736, 712, 421, 1044 | 960, 534, 396, 807, 141, 532, 900, 881, 33, 561, 142, 348, 379, 577, 792, 782, 653, 863, 954, 769, 1045 | 98, 662, 445, 640, 737, 675, 234, 798, 971, 140, 758, 434, 546, 313, 518, 842, 471, 49, 507, 87, 1046 | 426, 536, 704, 856, 402, 99, 356, 276, 381, 945, 558, 470, 67, 495, 500, 3, 346, 995, 934, 679, 1047 | 97, 817, 980, 497, 773, 84, 138, 57, 489, 902, 259, 167, 989, 229, 32, 591, 848, 651, 327, 440, 1048 | 407, 965, 418, 155, 488, 375, 151, 318, 272, 249, 44, 184, 60, 124, 691, 664, 416, 285, 35, 566, 1049 | 315, 94, 759, 820, 612, 436, 245, 779, 724, 217, 778, 983, 515, 160, 310, 186, 739, 452, 623, 8, 1050 | 975, 880, 631, 13, 330, 63, 126, 586, 253, 115, 885, 64, 374, 925, 485, 162, 931, 412, 146, 4, 1051 | 762, 102, 794, 770, 240, 244, 708, 969, 775, 206, 877, 859, 617, 409, 306, 80, 785, 621, 30, 166, 1052 | 795, 223, 723, 602, 814, 405, 24, 236, 531, 359, 130, 108, 411, 896, 647, 840, 425, 916, 116, 910, 1053 | 986, 38, 753, 395, 132, 457, 386, 725, 304, 196, 293, 873, 363, 898, 781, 808, 51, 267, 104, 110, 1054 | 510, 868, 291, 243, 527, 15, 182, 652, 905, 286, 109, 537, 706, 927, 79, 581, 825, 538, 178, 752, 1055 | 522, 255, 689, 462, 772, 658, 935, 603, 937, 545, 696, 743, 284, 620, 766, 892, 390, 735, 208, 728, 1056 | 202, 682, 879, 805, 171, 974, 941, 761, 526, 295, 432, 410, 731, 365, 793, 328, 251, 200, 136, 289, 1057 | 22, 784, 787, 741, 41, 437, 494, 9, 428, 660, 183, 150, 516, 329, 152, 451, 277, 332, 427, 872, 1058 | 634, 539, 325, 802, 567, 321, 764, 697, 947, 219, 282, 476, 275, 215, 336, 645, 923, 967, 970, 433, 1059 | 595, 23, 478, 139, 876, 673, 334, 438, 237, 574, 19, 530, 571, 665, 930, 353, 502, 297, 661, 886, 1060 | 672, 114, 100, 520, 547, 50, 204, 959, 392, 18, 82, 853, 678, 790, 684, 481, 331, 388, 1, 242, 1061 | 486, 514, 683, 922, 444, 118, 271, 302, 299, 232, 529, 839, 625, 908, 193, 391, 828, 875, 626, 68, 1062 | 619, 198, 27, 235, 891, 742, 907, 893, 657, 803, 439, 780, 812, 296, 278, 763, 127, 77, 504, 587, 1063 | 144, 978, 383, 174, 599, 149, 339, 670, 169, 508, 258, 168, 976, 369, 442, 940, 968, 459, 676, 399, 1064 | 263, 181, 841, 831, 575, 718, 58, 147, 280, 201, 48, 890, 377, 261, 337, 468, 588, 55, 668, 74, 1065 | 641, 604, 472, 69, 83, 350, 686, 789, 584, 726, 56, 826, 622, 42, 371, 129, 843, 711, 648, 824, 1066 | 512, 480, 475, 553, 929, 314, 783, 496, 91, 943, 944, 357, 768, 710, 797, 269, 300, 225, 344, 800, 1067 | 45, 135, 765, 915, 105, 197, 185, 12, 323, 341, 707, 605, 345, 322, 123, 637, 564, 717, 827, 846, 1068 | 594, 71, 227, 86, 148, 854, 228, 257, 268, 849, 888, 933, 834, 847, 373, 942, 423, 573, 207, 857, 1069 | 920, 61, 414, 493, 727, 443, 143, 431, 501, 597, 851, 153, 964, 973, 40, 813, 585, 303, 767, 343, 1070 | 194, 616, 419, 998, 544, 818, 845, 950, 819, 165, 319, 90, 714, 688, 172, 191, 422, 655, 644, 241, 1071 | 535, 563, 719, 525, 175, 569, 615, 294, 290, 0, 690, 962, 582, 961, 324, 125, 72, 747, 701, 590, 1072 | 693, 498, 596, 509, 932, 542, 946, 576, 156, 54, 429, 28, 133, 756, 65, 882, 716, 578, 384, 103, 1073 | 917, 17, 838, 21, 835, 654, 630, 796, 11, 274, 43, 119, 88, 400, 380, 746, 593, 266, 549, 994, 1074 | 685, 122, 106, 810, 799, 734, 830, 589, 903, 722, 750, 899, 984, 264, 479, 112, 987, 305, 26, 176, 1075 | 382, 464, 250, 121, 981, 499, 378, 745, 909, 466, 254, 389, 195, 137, 25, 220, 823, 482, 721, 473, 1076 | 952, 996, 335, 415, 47, 205, 656, 62, 966, 948, 883, 801, 757, 698, 870, 738, 10, 368, 287, 89, 1077 | 777, 614, 649, 992, 938, 600, 283, 180, 921, 555, 14, 107, 450, 312, 454, 513, 397, 210, 503, 424, 1078 | 669, 751, 447, 270, 288, 406, 861, 39, 52, 398, 695, 901, 864, 815, 461, 256, 748, 460, 579, 720, 1079 | 120, 666, 355, 401, 487, 613, 913, 430, 338, 633, 364, 592, 540, 550, 560, 692, 804, 157, 850, 36, 1080 | 610, 991, 349, 491, 744, 611, 326, 211, 477, 776, 224, 134, 833, 702, 521, 209, 247, 552, 760, 926, 1081 | 5, 188, 246, 238, 31, 37, 221, 298, 715, 393, 203, 230, 524, 370, 262, 96, 829, 786, 34, 629, 1082 | } 1083 | 1084 | indicesToDeleteNotNormalized := []int{ 1085 | 401, 426, 285, 997, 355, 109, 205, 89, 129, 698, 266, 102, 691, 676, 912, 223, 892, 93, 472, 870, 1086 | 904, 111, 560, 284, 438, 201, 863, 380, 368, 804, 341, 627, 273, 837, 577, 744, 934, 556, 208, 394, 1087 | 622, 121, 452, 773, 721, 287, 354, 981, 607, 349, 546, 467, 827, 670, 491, 221, 785, 407, 767, 794, 1088 | 835, 386, 339, 715, 758, 66, 891, 62, 531, 399, 135, 747, 493, 122, 460, 445, 821, 115, 194, 310, 1089 | 695, 651, 542, 791, 602, 327, 434, 113, 965, 889, 149, 834, 866, 799, 127, 592, 324, 841, 732, 857, 1090 | 280, 489, 190, 663, 389, 55, 131, 914, 911, 992, 453, 249, 473, 126, 532, 883, 40, 660, 902, 949, 1091 | 648, 463, 533, 924, 764, 270, 748, 646, 567, 207, 613, 749, 690, 158, 793, 810, 935, 45, 243, 325, 1092 | 621, 179, 301, 543, 1, 563, 151, 15, 605, 825, 862, 229, 147, 406, 357, 847, 403, 165, 510, 882, 1093 | 206, 888, 124, 983, 261, 508, 417, 59, 554, 218, 653, 669, 673, 334, 236, 860, 38, 788, 81, 629, 1094 | 163, 523, 373, 790, 631, 858, 329, 838, 496, 143, 9, 734, 649, 500, 502, 142, 263, 971, 780, 365, 1095 | 828, 961, 782, 720, 872, 64, 309, 986, 941, 367, 247, 798, 322, 672, 364, 177, 829, 896, 609, 430, 1096 | 719, 958, 960, 947, 561, 645, 382, 604, 976, 182, 569, 29, 275, 282, 343, 288, 279, 683, 134, 574, 1097 | 848, 878, 110, 686, 18, 926, 763, 849, 362, 487, 19, 675, 369, 375, 232, 474, 217, 746, 702, 479, 1098 | 998, 742, 518, 737, 446, 659, 707, 624, 139, 595, 145, 515, 411, 995, 630, 514, 724, 899, 754, 522, 1099 | 548, 776, 335, 240, 634, 765, 880, 8, 433, 571, 257, 503, 704, 191, 224, 449, 0, 685, 429, 710, 1100 | 465, 214, 276, 950, 374, 713, 133, 658, 617, 919, 447, 643, 117, 787, 688, 930, 49, 83, 32, 740, 1101 | 332, 895, 340, 920, 501, 192, 466, 277, 220, 50, 119, 844, 733, 931, 311, 652, 418, 637, 566, 245, 1102 | 39, 726, 91, 86, 456, 778, 678, 226, 730, 23, 781, 450, 408, 222, 267, 783, 802, 819, 869, 956, 1103 | 30, 231, 527, 5, 680, 576, 922, 168, 657, 204, 547, 372, 199, 314, 10, 552, 295, 517, 306, 852, 1104 | 432, 545, 215, 692, 202, 97, 471, 925, 786, 957, 760, 99, 164, 384, 519, 751, 260, 756, 936, 25, 1105 | 771, 485, 570, 770, 11, 632, 120, 94, 761, 216, 865, 409, 606, 65, 937, 709, 457, 13, 60, 455, 1106 | 197, 877, 772, 498, 854, 69, 696, 932, 475, 251, 213, 184, 855, 582, 893, 537, 58, 264, 903, 499, 1107 | 693, 973, 714, 969, 839, 152, 371, 655, 991, 809, 167, 289, 316, 705, 379, 513, 544, 106, 599, 635, 1108 | 584, 415, 644, 146, 846, 753, 796, 52, 393, 731, 128, 979, 815, 913, 587, 940, 7, 588, 398, 271, 1109 | 808, 189, 647, 559, 738, 47, 641, 36, 851, 148, 203, 681, 114, 774, 982, 274, 942, 336, 317, 187, 1110 | 529, 464, 939, 370, 901, 917, 422, 573, 951, 811, 253, 3, 853, 654, 874, 234, 272, 342, 668, 459, 1111 | 308, 56, 505, 520, 541, 777, 727, 431, 150, 12, 591, 188, 616, 20, 974, 43, 153, 454, 557, 24, 1112 | 323, 944, 166, 915, 256, 843, 173, 425, 977, 994, 970, 581, 154, 416, 268, 141, 509, 305, 181, 701, 1113 | 968, 708, 980, 642, 484, 252, 293, 26, 212, 259, 687, 822, 82, 528, 75, 290, 909, 361, 580, 227, 1114 | 183, 27, 535, 37, 536, 640, 414, 694, 112, 2, 755, 871, 486, 469, 550, 16, 41, 511, 564, 359, 1115 | 598, 775, 752, 84, 521, 477, 745, 178, 294, 832, 331, 125, 820, 176, 427, 291, 679, 608, 568, 897, 1116 | 307, 497, 993, 792, 73, 814, 468, 697, 623, 850, 516, 578, 611, 174, 666, 377, 875, 14, 123, 881, 1117 | 948, 144, 378, 779, 262, 723, 155, 320, 989, 964, 625, 583, 70, 71, 650, 241, 244, 302, 248, 990, 1118 | 138, 17, 87, 35, 795, 79, 400, 428, 439, 237, 718, 57, 955, 200, 929, 419, 999, 363, 927, 555, 1119 | 988, 836, 876, 347, 830, 77, 656, 195, 476, 51, 962, 265, 387, 209, 423, 76, 879, 868, 867, 762, 1120 | 945, 985, 633, 421, 908, 90, 717, 255, 228, 711, 789, 196, 330, 344, 558, 906, 298, 619, 171, 590, 1121 | 766, 108, 328, 699, 198, 392, 482, 938, 921, 600, 596, 918, 246, 424, 22, 859, 539, 861, 488, 180, 1122 | 618, 356, 412, 440, 436, 898, 104, 437, 304, 589, 887, 894, 159, 95, 856, 390, 160, 769, 553, 725, 1123 | 628, 565, 864, 46, 492, 481, 286, 483, 413, 716, 299, 996, 638, 254, 943, 884, 296, 338, 105, 100, 1124 | 689, 818, 886, 444, 671, 478, 297, 959, 494, 750, 405, 806, 420, 953, 353, 910, 319, 92, 281, 684, 1125 | 85, 185, 443, 230, 395, 300, 157, 923, 250, 967, 383, 768, 313, 601, 757, 594, 603, 480, 526, 946, 1126 | 283, 549, 451, 636, 842, 845, 53, 258, 80, 639, 495, 612, 524, 6, 840, 817, 729, 101, 562, 972, 1127 | 21, 784, 103, 512, 736, 741, 610, 219, 728, 807, 350, 156, 402, 933, 873, 470, 54, 388, 812, 345, 1128 | 954, 885, 118, 74, 579, 572, 116, 441, 435, 397, 4, 739, 805, 759, 448, 352, 551, 333, 615, 620, 1129 | 905, 797, 278, 161, 900, 42, 907, 211, 44, 677, 28, 137, 385, 391, 186, 233, 661, 321, 34, 540, 1130 | 396, 292, 801, 72, 63, 890, 813, 831, 816, 210, 735, 225, 823, 130, 490, 175, 963, 743, 978, 534, 1131 | 530, 667, 162, 442, 824, 593, 303, 803, 800, 140, 239, 507, 966, 312, 242, 664, 337, 269, 665, 78, 1132 | 366, 381, 67, 61, 132, 462, 315, 461, 506, 700, 326, 916, 107, 706, 674, 504, 98, 525, 172, 928, 1133 | 586, 193, 585, 575, 376, 833, 712, 235, 238, 987, 722, 404, 346, 682, 31, 48, 458, 662, 68, 88, 1134 | 952, 169, 597, 136, 360, 703, 984, 410, 96, 348, 33, 614, 170, 318, 351, 538, 826, 626, 358, 975, 1135 | } 1136 | 1137 | testInsertGetDeleteByIndex(t, tree, keysToInsert, indicesToGet, indicesToDeleteNotNormalized) 1138 | } 1139 | 1140 | func metaTestInsertGetDeleteByIndexHuge(t *testing.T, tree SortedMap) { 1141 | var ( 1142 | err error 1143 | indicesToDeleteNotNormalized []int 1144 | indicesToGet []int 1145 | keysToInsert []int 1146 | ) 1147 | 1148 | keysToInsert, err = testKnuthShuffledIntSlice(testHugeNumKeys) 1149 | if err != nil { 1150 | t.Fatal(err) 1151 | } 1152 | indicesToGet, err = testKnuthShuffledIntSlice(testHugeNumKeys) 1153 | if err != nil { 1154 | t.Fatal(err) 1155 | } 1156 | indicesToDeleteNotNormalized, err = testKnuthShuffledIntSlice(testHugeNumKeys) 1157 | if err != nil { 1158 | t.Fatal(err) 1159 | } 1160 | 1161 | testInsertGetDeleteByIndex(t, tree, keysToInsert, indicesToGet, indicesToDeleteNotNormalized) 1162 | } 1163 | 1164 | func testInsertGetDeleteByKey(t *testing.T, tree SortedMap, keysToInsert, keysToGet, keysToDelete []int) { 1165 | var ( 1166 | err error 1167 | keyToDelete int 1168 | keyToGet int 1169 | keyToInsert int 1170 | numberOfItems int 1171 | ok bool 1172 | valueAsString string 1173 | valueAsValue Value 1174 | ) 1175 | 1176 | numberOfItems, err = tree.Len() 1177 | if err != nil { 1178 | t.Fatal(err) 1179 | } 1180 | if numberOfItems != 0 { 1181 | t.Fatalf("Len() [Case 1] should have been 0... instead it was %v", numberOfItems) 1182 | } 1183 | 1184 | for _, keyToInsert = range keysToInsert { 1185 | valueAsString = strconv.Itoa(keyToInsert) 1186 | ok, err = tree.Put(keyToInsert, valueAsString) 1187 | if err != nil { 1188 | t.Fatal(err) 1189 | } 1190 | if !ok { 1191 | t.Fatalf("Put(%v, \"%v\").ok should have been true", keyToInsert, valueAsString) 1192 | } 1193 | } 1194 | 1195 | numberOfItems, err = tree.Len() 1196 | if err != nil { 1197 | t.Fatal(err) 1198 | } 1199 | if len(keysToInsert) != numberOfItems { 1200 | t.Fatalf("Len() [Case 2] should have been %v... instead it was %v", len(keysToInsert), numberOfItems) 1201 | } 1202 | 1203 | for _, keyToGet = range keysToGet { 1204 | valueAsValue, ok, err = tree.GetByKey(keyToGet) 1205 | if err != nil { 1206 | t.Fatal(err) 1207 | } 1208 | if !ok { 1209 | t.Fatalf("GetByKey(%v).ok should have been true", keyToGet) 1210 | } 1211 | valueAsString = valueAsValue.(string) 1212 | if strconv.Itoa(keyToGet) != valueAsString { 1213 | t.Fatalf("GetByKey(%v).value should have been \"%v\"... instead it was \"%v\"", keyToGet, strconv.Itoa(keyToGet), valueAsString) 1214 | } 1215 | } 1216 | 1217 | for _, keyToDelete = range keysToDelete { 1218 | ok, err = tree.DeleteByKey(keyToDelete) 1219 | if err != nil { 1220 | t.Fatal(err) 1221 | } 1222 | if !ok { 1223 | t.Fatalf("DeleteByKey(%v).ok should have been true", keyToDelete) 1224 | } 1225 | } 1226 | 1227 | numberOfItems, err = tree.Len() 1228 | if err != nil { 1229 | t.Fatal(err) 1230 | } 1231 | if numberOfItems != 0 { 1232 | t.Fatalf("Len() [Case 3] should have been %v... instead it was %v", 0, numberOfItems) 1233 | } 1234 | } 1235 | 1236 | func metaTestInsertGetDeleteByKeyTrivial(t *testing.T, tree SortedMap) { 1237 | keysToInsert := []int{ 1238 | 6, 3, 5, 2, 4, 0, 1, 1239 | } 1240 | 1241 | keysToGet := []int{ 1242 | 5, 3, 2, 6, 1, 0, 4, 1243 | } 1244 | 1245 | keysToDelete := []int{ 1246 | 5, 4, 1, 6, 2, 0, 3, 1247 | } 1248 | 1249 | testInsertGetDeleteByKey(t, tree, keysToInsert, keysToGet, keysToDelete) 1250 | } 1251 | 1252 | func metaTestInsertGetDeleteByKeySmall(t *testing.T, tree SortedMap) { 1253 | keysToInsert := []int{ 1254 | 4, 25, 38, 14, 36, 8, 6, 37, 23, 13, 24, 22, 5, 18, 39, 3, 7, 19, 26, 21, 1255 | 12, 32, 35, 30, 28, 11, 15, 20, 33, 9, 31, 1, 29, 16, 17, 27, 2, 34, 0, 10, 1256 | } 1257 | 1258 | keysToGet := []int{ 1259 | 9, 20, 37, 11, 5, 36, 29, 32, 21, 3, 0, 24, 23, 25, 26, 19, 33, 38, 28, 1, 1260 | 14, 18, 10, 16, 4, 8, 31, 12, 13, 39, 34, 35, 27, 2, 17, 22, 15, 30, 7, 6, 1261 | } 1262 | 1263 | keysToDelete := []int{ 1264 | 37, 29, 21, 38, 10, 16, 22, 33, 5, 31, 23, 1, 8, 36, 0, 17, 24, 35, 3, 34, 1265 | 11, 14, 6, 12, 30, 15, 18, 19, 20, 26, 25, 27, 7, 4, 2, 13, 32, 28, 39, 9, 1266 | } 1267 | 1268 | testInsertGetDeleteByKey(t, tree, keysToInsert, keysToGet, keysToDelete) 1269 | } 1270 | 1271 | func metaTestInsertGetDeleteByKeyLarge(t *testing.T, tree SortedMap) { 1272 | keysToInsert := []int{ 1273 | 360, 500, 214, 811, 371, 388, 174, 143, 643, 39, 402, 121, 926, 565, 516, 324, 693, 486, 591, 45, 1274 | 637, 328, 948, 97, 284, 738, 474, 103, 702, 91, 168, 233, 891, 351, 303, 253, 134, 62, 119, 387, 1275 | 211, 579, 787, 484, 635, 480, 26, 836, 426, 227, 319, 285, 874, 990, 323, 118, 369, 676, 666, 286, 1276 | 512, 445, 869, 208, 341, 536, 147, 246, 821, 255, 482, 419, 372, 716, 293, 646, 457, 60, 32, 683, 1277 | 639, 8, 979, 425, 262, 854, 145, 23, 146, 361, 695, 191, 852, 884, 352, 104, 808, 412, 597, 265, 1278 | 796, 789, 526, 271, 392, 933, 819, 82, 910, 222, 617, 440, 952, 681, 633, 704, 653, 823, 59, 140, 1279 | 87, 337, 217, 655, 871, 909, 699, 764, 511, 545, 66, 346, 533, 469, 289, 30, 473, 332, 729, 428, 1280 | 650, 98, 55, 580, 837, 607, 263, 577, 551, 834, 169, 712, 304, 620, 588, 604, 947, 16, 160, 43, 1281 | 862, 970, 397, 747, 772, 478, 905, 112, 307, 899, 52, 520, 158, 880, 357, 766, 602, 706, 968, 75, 1282 | 374, 89, 701, 609, 574, 898, 186, 636, 6, 612, 578, 395, 476, 94, 14, 730, 592, 441, 902, 252, 1283 | 25, 300, 674, 251, 198, 355, 325, 781, 266, 936, 282, 786, 826, 317, 627, 889, 878, 40, 713, 553, 1284 | 443, 783, 56, 182, 349, 7, 561, 68, 254, 813, 501, 162, 329, 930, 149, 105, 816, 928, 753, 133, 1285 | 861, 761, 490, 645, 931, 971, 760, 237, 629, 988, 224, 677, 111, 276, 774, 108, 172, 468, 399, 539, 1286 | 830, 405, 279, 142, 991, 385, 151, 814, 339, 321, 508, 546, 630, 858, 302, 689, 275, 529, 389, 472, 1287 | 563, 965, 35, 820, 193, 758, 380, 458, 995, 998, 980, 258, 461, 961, 442, 27, 708, 647, 594, 465, 1288 | 471, 280, 295, 434, 453, 801, 163, 567, 648, 914, 771, 36, 175, 542, 756, 964, 489, 483, 364, 743, 1289 | 264, 67, 590, 958, 665, 113, 779, 844, 803, 657, 135, 406, 583, 76, 1, 966, 220, 535, 913, 907, 1290 | 225, 245, 759, 682, 203, 236, 256, 173, 316, 731, 114, 924, 865, 197, 784, 344, 308, 309, 644, 589, 1291 | 736, 954, 109, 851, 515, 155, 918, 872, 305, 138, 96, 800, 390, 628, 850, 873, 290, 573, 959, 330, 1292 | 268, 239, 327, 368, 559, 335, 944, 749, 797, 576, 69, 514, 841, 446, 943, 73, 269, 622, 226, 722, 1293 | 805, 866, 877, 449, 587, 610, 4, 171, 863, 216, 292, 86, 977, 895, 444, 13, 9, 47, 497, 31, 1294 | 433, 624, 812, 11, 987, 270, 843, 616, 88, 903, 656, 462, 185, 950, 177, 394, 503, 775, 117, 54, 1295 | 939, 815, 593, 890, 223, 417, 291, 299, 3, 973, 322, 829, 946, 84, 189, 125, 518, 196, 378, 240, 1296 | 431, 929, 564, 975, 178, 243, 116, 248, 450, 493, 983, 853, 272, 101, 2, 488, 993, 343, 420, 984, 1297 | 170, 126, 562, 822, 557, 703, 123, 919, 167, 632, 297, 601, 157, 626, 780, 424, 527, 818, 810, 259, 1298 | 456, 556, 99, 479, 205, 460, 652, 187, 427, 839, 48, 544, 447, 79, 439, 669, 798, 274, 437, 634, 1299 | 466, 684, 548, 71, 999, 298, 65, 510, 555, 922, 336, 925, 312, 418, 967, 904, 882, 521, 532, 807, 1300 | 375, 762, 213, 37, 107, 238, 896, 288, 61, 651, 696, 917, 832, 688, 502, 435, 600, 584, 77, 78, 1301 | 403, 148, 969, 744, 353, 407, 358, 596, 625, 391, 838, 694, 124, 29, 900, 710, 481, 53, 725, 206, 1302 | 680, 413, 311, 739, 568, 241, 746, 247, 448, 799, 949, 393, 897, 752, 932, 95, 566, 715, 795, 42, 1303 | 921, 997, 506, 129, 957, 188, 570, 912, 221, 719, 938, 230, 164, 927, 467, 523, 868, 18, 331, 642, 1304 | 751, 202, 320, 90, 530, 986, 672, 70, 356, 953, 314, 156, 794, 631, 859, 470, 621, 835, 429, 664, 1305 | 846, 190, 908, 244, 44, 773, 883, 989, 20, 732, 411, 165, 423, 613, 901, 306, 714, 273, 209, 522, 1306 | 141, 916, 345, 934, 709, 534, 857, 718, 864, 338, 379, 598, 498, 74, 137, 847, 242, 575, 430, 791, 1307 | 152, 605, 207, 608, 734, 509, 340, 654, 785, 940, 692, 849, 366, 347, 408, 195, 845, 763, 793, 120, 1308 | 100, 28, 543, 641, 817, 586, 161, 477, 17, 115, 487, 662, 414, 558, 278, 235, 982, 128, 310, 454, 1309 | 277, 136, 945, 260, 24, 606, 809, 686, 782, 742, 720, 199, 881, 102, 618, 400, 802, 459, 212, 313, 1310 | 582, 721, 824, 382, 827, 15, 828, 671, 726, 717, 485, 996, 386, 767, 875, 58, 363, 153, 724, 267, 1311 | 690, 232, 955, 733, 249, 867, 318, 377, 507, 750, 410, 885, 778, 51, 994, 21, 49, 892, 376, 687, 1312 | 768, 792, 416, 334, 513, 92, 234, 455, 735, 915, 661, 976, 150, 740, 963, 770, 700, 370, 12, 599, 1313 | 524, 110, 404, 673, 492, 741, 106, 0, 184, 707, 705, 937, 840, 261, 93, 83, 886, 906, 876, 549, 1314 | 727, 640, 401, 127, 691, 833, 525, 855, 496, 615, 550, 856, 848, 396, 728, 737, 139, 581, 144, 180, 1315 | 697, 757, 537, 315, 132, 257, 19, 491, 519, 350, 72, 769, 679, 294, 667, 499, 860, 281, 5, 870, 1316 | 888, 154, 475, 130, 166, 367, 204, 438, 540, 122, 531, 560, 804, 538, 978, 228, 326, 748, 920, 451, 1317 | 34, 384, 663, 436, 80, 825, 745, 755, 923, 215, 194, 893, 552, 659, 571, 879, 229, 754, 432, 296, 1318 | 678, 33, 974, 505, 675, 595, 495, 159, 517, 981, 831, 452, 250, 287, 183, 192, 46, 85, 985, 619, 1319 | 765, 22, 894, 603, 960, 806, 421, 956, 623, 41, 50, 658, 359, 231, 63, 210, 373, 572, 685, 365, 1320 | 611, 348, 788, 668, 333, 842, 585, 541, 941, 528, 711, 776, 614, 383, 381, 911, 38, 64, 951, 200, 1321 | 887, 398, 354, 81, 649, 10, 494, 463, 660, 790, 179, 569, 219, 504, 176, 283, 181, 972, 201, 777, 1322 | 301, 415, 942, 218, 342, 962, 57, 723, 464, 409, 935, 131, 670, 698, 992, 554, 638, 547, 362, 422, 1323 | } 1324 | 1325 | keysToGet := []int{ 1326 | 134, 105, 783, 731, 203, 206, 367, 375, 374, 233, 370, 390, 384, 386, 581, 938, 680, 66, 606, 503, 1327 | 673, 65, 907, 425, 948, 38, 924, 790, 651, 246, 767, 87, 331, 63, 114, 103, 758, 573, 833, 724, 1328 | 592, 723, 761, 264, 985, 966, 1, 16, 825, 822, 746, 502, 937, 43, 57, 515, 996, 401, 662, 191, 1329 | 149, 73, 294, 800, 696, 458, 796, 908, 929, 473, 975, 660, 863, 976, 163, 896, 942, 282, 388, 274, 1330 | 623, 693, 603, 485, 612, 97, 110, 675, 765, 268, 669, 18, 795, 993, 220, 116, 977, 71, 779, 378, 1331 | 809, 142, 476, 855, 711, 186, 946, 72, 815, 210, 395, 793, 85, 974, 479, 340, 145, 702, 755, 70, 1332 | 199, 4, 392, 144, 208, 526, 371, 894, 205, 236, 415, 970, 570, 979, 180, 122, 358, 928, 745, 450, 1333 | 195, 377, 824, 313, 296, 322, 609, 578, 645, 131, 794, 194, 460, 311, 527, 906, 911, 64, 949, 360, 1334 | 154, 380, 598, 40, 667, 127, 368, 640, 898, 757, 548, 287, 281, 681, 62, 915, 193, 29, 469, 437, 1335 | 537, 89, 487, 42, 870, 982, 814, 997, 823, 525, 507, 709, 136, 497, 841, 452, 20, 862, 185, 647, 1336 | 569, 876, 935, 226, 602, 737, 599, 56, 834, 77, 555, 936, 878, 466, 742, 55, 442, 330, 188, 690, 1337 | 983, 943, 309, 817, 319, 786, 963, 334, 931, 871, 840, 644, 955, 664, 575, 543, 506, 829, 336, 495, 1338 | 558, 540, 492, 266, 227, 980, 972, 799, 832, 721, 51, 33, 31, 501, 546, 234, 860, 510, 990, 912, 1339 | 480, 760, 616, 850, 961, 169, 225, 184, 351, 299, 108, 327, 778, 654, 678, 159, 491, 212, 315, 784, 1340 | 797, 706, 560, 918, 653, 308, 686, 272, 725, 133, 633, 818, 280, 302, 774, 753, 682, 455, 712, 436, 1341 | 423, 951, 826, 532, 434, 239, 463, 329, 3, 583, 28, 365, 262, 92, 76, 749, 198, 574, 533, 332, 1342 | 152, 454, 421, 917, 459, 98, 934, 317, 446, 720, 276, 173, 600, 123, 534, 90, 893, 978, 24, 754, 1343 | 32, 192, 559, 632, 7, 362, 944, 615, 626, 182, 801, 889, 872, 60, 216, 547, 947, 729, 636, 300, 1344 | 591, 190, 554, 759, 597, 248, 104, 519, 94, 260, 167, 27, 670, 699, 472, 617, 768, 0, 400, 160, 1345 | 483, 235, 341, 542, 932, 897, 461, 237, 403, 646, 484, 763, 728, 214, 39, 812, 811, 86, 14, 539, 1346 | 95, 451, 722, 921, 482, 504, 914, 125, 756, 930, 572, 905, 953, 223, 880, 324, 164, 427, 2, 414, 1347 | 541, 625, 426, 847, 422, 715, 429, 158, 325, 270, 54, 364, 788, 320, 701, 950, 883, 895, 46, 298, 1348 | 156, 957, 74, 727, 676, 610, 433, 130, 807, 836, 157, 744, 448, 478, 819, 475, 297, 165, 113, 81, 1349 | 453, 346, 68, 293, 595, 565, 659, 207, 168, 716, 576, 391, 762, 810, 456, 691, 88, 431, 582, 366, 1350 | 333, 196, 717, 605, 404, 498, 713, 959, 577, 481, 962, 221, 430, 137, 521, 243, 129, 126, 994, 93, 1351 | 677, 111, 381, 441, 652, 356, 279, 449, 290, 666, 416, 604, 656, 695, 328, 200, 435, 580, 357, 698, 1352 | 747, 355, 508, 359, 326, 536, 283, 694, 842, 224, 621, 998, 764, 751, 493, 301, 269, 649, 769, 967, 1353 | 968, 925, 58, 117, 67, 204, 881, 770, 48, 289, 8, 21, 965, 657, 271, 247, 511, 838, 242, 563, 1354 | 509, 5, 629, 910, 628, 396, 304, 228, 82, 844, 890, 517, 531, 30, 901, 553, 78, 310, 566, 373, 1355 | 353, 514, 10, 488, 187, 879, 887, 102, 44, 868, 162, 846, 124, 471, 608, 464, 909, 791, 885, 750, 1356 | 372, 665, 813, 153, 172, 383, 703, 902, 954, 743, 69, 941, 882, 552, 256, 278, 989, 251, 697, 892, 1357 | 432, 535, 867, 306, 904, 342, 618, 960, 586, 923, 338, 151, 369, 848, 927, 26, 601, 550, 79, 719, 1358 | 345, 143, 291, 50, 500, 229, 843, 112, 831, 101, 588, 178, 622, 614, 474, 658, 856, 420, 584, 835, 1359 | 316, 671, 851, 250, 804, 211, 286, 874, 35, 828, 973, 244, 945, 339, 820, 589, 837, 213, 643, 642, 1360 | 288, 619, 255, 240, 23, 940, 562, 80, 861, 303, 505, 419, 620, 926, 674, 789, 494, 470, 385, 399, 1361 | 261, 873, 209, 805, 347, 900, 952, 991, 752, 875, 138, 335, 179, 45, 986, 964, 556, 418, 551, 594, 1362 | 321, 638, 350, 467, 181, 740, 704, 135, 639, 587, 217, 679, 869, 726, 115, 992, 648, 413, 232, 808, 1363 | 343, 672, 522, 379, 109, 292, 688, 710, 84, 866, 849, 382, 933, 13, 798, 513, 34, 821, 218, 61, 1364 | 408, 411, 999, 428, 913, 865, 512, 139, 780, 277, 516, 650, 524, 83, 305, 17, 438, 468, 852, 59, 1365 | 47, 41, 176, 15, 718, 987, 634, 132, 518, 174, 830, 398, 732, 585, 397, 490, 689, 920, 22, 741, 1366 | 884, 663, 37, 772, 314, 637, 462, 611, 776, 91, 275, 361, 285, 827, 641, 613, 523, 734, 477, 318, 1367 | 969, 781, 201, 668, 736, 36, 349, 995, 528, 147, 624, 254, 687, 457, 984, 888, 417, 363, 96, 189, 1368 | 263, 545, 630, 175, 215, 627, 845, 886, 273, 258, 730, 202, 443, 579, 354, 981, 561, 161, 708, 496, 1369 | 520, 197, 252, 409, 499, 100, 792, 19, 170, 971, 267, 249, 692, 146, 445, 222, 714, 107, 775, 53, 1370 | 568, 171, 700, 253, 12, 307, 141, 988, 6, 11, 891, 782, 52, 106, 344, 567, 857, 312, 593, 120, 1371 | 766, 389, 707, 785, 230, 444, 238, 738, 387, 402, 118, 155, 899, 99, 265, 922, 439, 859, 631, 231, 1372 | 685, 903, 394, 121, 9, 352, 177, 49, 284, 140, 939, 245, 148, 771, 424, 549, 465, 777, 803, 376, 1373 | 348, 337, 150, 544, 683, 733, 655, 916, 259, 407, 447, 393, 596, 538, 219, 25, 787, 661, 295, 773, 1374 | 323, 489, 748, 241, 410, 956, 684, 257, 75, 858, 529, 571, 864, 816, 854, 739, 635, 564, 735, 183, 1375 | 440, 839, 128, 705, 405, 877, 853, 958, 486, 166, 590, 919, 412, 530, 806, 119, 406, 802, 607, 557, 1376 | } 1377 | 1378 | keysToDelete := []int{ 1379 | 910, 109, 39, 984, 833, 364, 905, 90, 236, 226, 27, 262, 10, 292, 16, 40, 918, 516, 737, 654, 1380 | 741, 604, 760, 228, 688, 398, 791, 339, 979, 71, 610, 514, 354, 729, 225, 399, 32, 715, 773, 134, 1381 | 940, 468, 31, 975, 486, 380, 735, 93, 673, 220, 59, 806, 67, 851, 107, 382, 247, 991, 63, 306, 1382 | 204, 70, 436, 389, 561, 764, 942, 4, 782, 593, 877, 565, 158, 903, 885, 985, 796, 283, 229, 552, 1383 | 542, 254, 811, 125, 338, 600, 156, 599, 923, 607, 700, 634, 208, 725, 371, 471, 161, 913, 210, 503, 1384 | 274, 901, 0, 42, 428, 768, 350, 407, 420, 995, 457, 929, 914, 684, 922, 636, 145, 46, 482, 934, 1385 | 149, 319, 794, 506, 863, 320, 470, 619, 697, 34, 438, 381, 100, 151, 529, 936, 761, 459, 854, 852, 1386 | 206, 429, 315, 360, 372, 275, 460, 499, 390, 35, 803, 683, 268, 500, 286, 825, 614, 590, 401, 237, 1387 | 114, 272, 82, 784, 223, 778, 195, 685, 767, 895, 217, 259, 269, 549, 844, 449, 163, 563, 647, 906, 1388 | 157, 489, 451, 800, 703, 576, 886, 678, 992, 188, 164, 391, 505, 621, 810, 301, 993, 246, 944, 871, 1389 | 186, 233, 116, 598, 596, 96, 442, 435, 196, 64, 14, 308, 173, 876, 140, 592, 44, 686, 570, 419, 1390 | 881, 450, 569, 628, 495, 692, 61, 902, 815, 143, 47, 702, 652, 263, 521, 935, 608, 771, 234, 836, 1391 | 425, 238, 579, 279, 256, 2, 564, 805, 517, 744, 841, 508, 240, 343, 74, 129, 13, 527, 494, 483, 1392 | 756, 988, 526, 952, 752, 545, 819, 106, 332, 551, 285, 759, 953, 541, 490, 554, 169, 130, 431, 245, 1393 | 329, 342, 84, 965, 484, 763, 960, 19, 487, 548, 755, 282, 855, 242, 126, 112, 980, 235, 712, 578, 1394 | 799, 75, 613, 37, 353, 884, 253, 243, 101, 640, 996, 967, 890, 369, 830, 566, 102, 696, 518, 141, 1395 | 458, 861, 440, 418, 406, 3, 727, 230, 730, 974, 732, 657, 945, 138, 898, 290, 894, 154, 645, 146, 1396 | 160, 293, 362, 972, 379, 11, 588, 872, 629, 300, 105, 642, 582, 550, 547, 675, 618, 325, 252, 211, 1397 | 99, 785, 172, 655, 726, 997, 433, 51, 153, 973, 417, 994, 239, 312, 18, 232, 954, 783, 258, 774, 1398 | 405, 409, 711, 113, 36, 926, 679, 777, 288, 941, 983, 241, 757, 637, 395, 891, 133, 699, 216, 577, 1399 | 908, 558, 832, 203, 190, 219, 981, 135, 375, 522, 513, 808, 971, 424, 139, 625, 171, 168, 707, 573, 1400 | 672, 412, 746, 314, 840, 348, 747, 244, 804, 257, 427, 920, 387, 769, 617, 530, 739, 817, 580, 858, 1401 | 875, 313, 656, 605, 396, 693, 631, 650, 691, 53, 638, 731, 131, 606, 897, 304, 465, 751, 641, 595, 1402 | 860, 86, 199, 643, 609, 176, 276, 123, 322, 835, 633, 368, 690, 5, 481, 583, 662, 45, 915, 358, 1403 | 152, 260, 887, 668, 192, 964, 850, 477, 868, 365, 207, 128, 795, 649, 469, 148, 108, 862, 480, 214, 1404 | 816, 231, 962, 414, 170, 439, 961, 432, 281, 66, 404, 147, 809, 334, 528, 136, 911, 540, 103, 184, 1405 | 758, 9, 48, 183, 397, 651, 49, 77, 603, 373, 20, 644, 175, 317, 728, 179, 888, 91, 781, 415, 1406 | 813, 770, 689, 97, 62, 21, 879, 277, 52, 79, 670, 969, 81, 553, 639, 509, 218, 488, 430, 434, 1407 | 762, 669, 826, 127, 626, 215, 182, 41, 525, 352, 958, 8, 174, 132, 65, 560, 698, 85, 821, 532, 1408 | 250, 706, 534, 904, 305, 273, 115, 270, 987, 765, 925, 6, 422, 421, 776, 296, 416, 25, 298, 403, 1409 | 197, 366, 648, 775, 165, 278, 568, 845, 680, 660, 591, 912, 507, 622, 321, 955, 848, 284, 831, 710, 1410 | 57, 98, 865, 213, 318, 581, 408, 870, 72, 512, 538, 559, 392, 497, 316, 294, 462, 667, 349, 536, 1411 | 466, 110, 76, 812, 456, 562, 713, 221, 324, 742, 722, 12, 571, 839, 370, 843, 820, 83, 227, 56, 1412 | 892, 681, 271, 177, 478, 287, 120, 515, 772, 351, 295, 87, 630, 411, 661, 426, 191, 947, 705, 907, 1413 | 531, 155, 899, 646, 446, 586, 22, 585, 202, 704, 212, 441, 823, 30, 917, 708, 524, 201, 330, 878, 1414 | 829, 336, 574, 376, 394, 748, 846, 519, 695, 537, 323, 50, 664, 663, 24, 827, 849, 266, 310, 594, 1415 | 472, 932, 999, 801, 327, 724, 124, 866, 714, 344, 556, 209, 454, 535, 198, 144, 834, 80, 137, 786, 1416 | 180, 251, 749, 501, 29, 867, 355, 122, 789, 818, 677, 88, 159, 882, 92, 443, 718, 200, 880, 118, 1417 | 779, 721, 474, 620, 717, 694, 95, 335, 189, 740, 43, 38, 873, 584, 822, 745, 473, 957, 367, 94, 1418 | 793, 939, 491, 959, 384, 671, 111, 356, 69, 989, 951, 224, 597, 511, 736, 950, 437, 963, 341, 187, 1419 | 193, 627, 575, 185, 28, 567, 400, 682, 453, 248, 938, 864, 924, 889, 587, 167, 533, 828, 589, 485, 1420 | 766, 687, 790, 117, 998, 104, 539, 990, 615, 328, 743, 893, 493, 23, 386, 701, 966, 601, 377, 857, 1421 | 26, 546, 309, 289, 255, 734, 787, 956, 792, 333, 249, 267, 738, 883, 331, 916, 150, 676, 659, 302, 1422 | 723, 346, 464, 635, 977, 520, 89, 68, 919, 222, 496, 900, 502, 479, 345, 265, 299, 814, 976, 162, 1423 | 510, 754, 837, 572, 467, 674, 359, 463, 946, 921, 1, 410, 933, 653, 750, 948, 874, 978, 383, 665, 1424 | 181, 297, 970, 303, 363, 602, 264, 194, 982, 476, 423, 807, 445, 543, 666, 927, 753, 73, 909, 611, 1425 | 78, 986, 7, 733, 60, 166, 455, 388, 385, 121, 357, 307, 802, 853, 557, 931, 797, 842, 311, 632, 1426 | 413, 444, 930, 859, 340, 856, 475, 261, 498, 448, 378, 780, 15, 928, 838, 623, 720, 709, 54, 402, 1427 | 326, 612, 788, 616, 798, 492, 943, 58, 178, 142, 624, 33, 824, 17, 452, 337, 968, 205, 544, 847, 1428 | 361, 347, 119, 280, 374, 461, 291, 949, 937, 896, 393, 447, 504, 716, 719, 658, 555, 869, 523, 55, 1429 | } 1430 | 1431 | testInsertGetDeleteByKey(t, tree, keysToInsert, keysToGet, keysToDelete) 1432 | } 1433 | 1434 | func metaTestInsertGetDeleteByKeyHuge(t *testing.T, tree SortedMap) { 1435 | var ( 1436 | err error 1437 | keysToDelete []int 1438 | keysToGet []int 1439 | keysToInsert []int 1440 | ) 1441 | 1442 | keysToInsert, err = testKnuthShuffledIntSlice(testHugeNumKeys) 1443 | if err != nil { 1444 | t.Fatal(err) 1445 | } 1446 | keysToGet, err = testKnuthShuffledIntSlice(testHugeNumKeys) 1447 | if err != nil { 1448 | t.Fatal(err) 1449 | } 1450 | keysToDelete, err = testKnuthShuffledIntSlice(testHugeNumKeys) 1451 | if err != nil { 1452 | t.Fatal(err) 1453 | } 1454 | 1455 | testInsertGetDeleteByKey(t, tree, keysToInsert, keysToGet, keysToDelete) 1456 | } 1457 | 1458 | func metaTestBisect(t *testing.T, tree SortedMap) { 1459 | var ( 1460 | bisectIndex int 1461 | bisectKey int 1462 | err error 1463 | found bool 1464 | key int 1465 | keyAsKey Key 1466 | keyExpected int 1467 | keyIndex int 1468 | keyIndices []int 1469 | ok bool 1470 | treeLen int 1471 | value string 1472 | ) 1473 | 1474 | // Insert testHugeNumKeys keys using ascending odd ints starting at int(1) 1475 | keyIndices, err = testKnuthShuffledIntSlice(testHugeNumKeys) 1476 | if err != nil { 1477 | t.Fatalf("Knuth Shuffle failed: %v", err) 1478 | } 1479 | 1480 | for _, keyIndex = range keyIndices { 1481 | key = (2 * keyIndex) + 1 1482 | value = strconv.Itoa(key) 1483 | ok, err = tree.Put(key, value) 1484 | if err != nil { 1485 | t.Fatal(err) 1486 | } 1487 | if !ok { 1488 | t.Fatalf("Put(%v, \"%v\").ok should have been true", key, value) 1489 | } 1490 | } 1491 | 1492 | // Verify tree now contains precisely these keys 1493 | treeLen, err = tree.Len() 1494 | if err != nil { 1495 | t.Fatal(err) 1496 | } 1497 | if testHugeNumKeys != treeLen { 1498 | t.Fatalf("Len() returned %v... expected %v", treeLen, testHugeNumKeys) 1499 | } 1500 | 1501 | for keyIndex = range testHugeNumKeys { 1502 | keyExpected = (2 * keyIndex) + 1 1503 | keyAsKey, _, ok, err = tree.GetByIndex(keyIndex) 1504 | if err != nil { 1505 | t.Fatal(err) 1506 | } 1507 | if !ok { 1508 | t.Fatalf("GetByIndex(%v).ok should have been true", keyIndex) 1509 | } 1510 | key = keyAsKey.(int) 1511 | if key != keyExpected { 1512 | t.Fatalf("GetByIndex(%v).key returned %v... expected %v", keyIndex, key, keyExpected) 1513 | } 1514 | } 1515 | 1516 | // Verify Bisect{Left|Right}() on each of these keys returns correct index & found == true 1517 | for keyIndex = range testHugeNumKeys { 1518 | bisectKey = (2 * keyIndex) + 1 1519 | 1520 | bisectIndex, found, err = tree.BisectLeft(bisectKey) 1521 | if err != nil { 1522 | t.Fatal(err) 1523 | } 1524 | if !found { 1525 | t.Fatalf("BisectLeft(%v).found should have been true", bisectKey) 1526 | } 1527 | if bisectIndex != keyIndex { 1528 | t.Fatalf("BisectLeft(%v).index returned %v... expected %v", bisectKey, bisectIndex, keyIndex) 1529 | } 1530 | 1531 | bisectIndex, found, err = tree.BisectRight(bisectKey) 1532 | if err != nil { 1533 | t.Fatal(err) 1534 | } 1535 | if !found { 1536 | t.Fatalf("BisectRight(%v).found should have been true", bisectKey) 1537 | } 1538 | if bisectIndex != keyIndex { 1539 | t.Fatalf("BisectRight(%v).index returned %v... expected %v", bisectKey, bisectIndex, keyIndex) 1540 | } 1541 | } 1542 | 1543 | // Verify Bisect{Left|Right}(0) returns correct index & found == false 1544 | bisectIndex, found, err = tree.BisectLeft(0) 1545 | if err != nil { 1546 | t.Fatal(err) 1547 | } 1548 | if found { 1549 | t.Fatalf("BisectLeft(0).found should have been false") 1550 | } 1551 | if bisectIndex != int(-1) { 1552 | t.Fatalf("BisectLeft(0).index returned %v... expected -1", bisectIndex) 1553 | } 1554 | 1555 | bisectIndex, found, err = tree.BisectRight(0) 1556 | if err != nil { 1557 | t.Fatal(err) 1558 | } 1559 | if found { 1560 | t.Fatalf("BisectRight(0).found should have been false") 1561 | } 1562 | if bisectIndex != 0 { 1563 | t.Fatalf("BisectRight(0).index returned %v... expected 0", bisectIndex) 1564 | } 1565 | 1566 | // Verify Bisect{Left|Right}() on each of these keys plus 1 (the following even ints) returns correct index & found == false 1567 | for keyIndex = range testHugeNumKeys { 1568 | bisectKey = (2 * keyIndex) + 1 + 1 1569 | 1570 | bisectIndex, found, err = tree.BisectLeft(bisectKey) 1571 | if err != nil { 1572 | t.Fatal(err) 1573 | } 1574 | if found { 1575 | t.Fatalf("BisectLeft(%v).found should have been false", bisectKey) 1576 | } 1577 | if bisectIndex != keyIndex { 1578 | t.Fatalf("BisectLeft(%v).index returned %v... expected %v", bisectKey, bisectIndex, keyIndex) 1579 | } 1580 | 1581 | bisectIndex, found, err = tree.BisectRight(bisectKey) 1582 | if err != nil { 1583 | t.Fatal(err) 1584 | } 1585 | if found { 1586 | t.Fatalf("BisectRight(%v).found should have been false", bisectKey) 1587 | } 1588 | if bisectIndex != (keyIndex + 1) { 1589 | t.Fatalf("BisectRight(%v).index returned %v... expected %v", bisectKey, bisectIndex, keyIndex) 1590 | } 1591 | } 1592 | } 1593 | 1594 | func metaBenchmarkPutStep(b *testing.B, tree SortedMap, keysToPut []int) { 1595 | var ( 1596 | err error 1597 | keyToPut int 1598 | ok bool 1599 | valueAsString string 1600 | ) 1601 | 1602 | for _, keyToPut = range keysToPut { 1603 | valueAsString = strconv.Itoa(keyToPut) 1604 | ok, err = tree.Put(keyToPut, valueAsString) 1605 | if err != nil { 1606 | err = fmt.Errorf("Put() returned unexpected error: %v", err) 1607 | b.Fatal(err) 1608 | } 1609 | if !ok { 1610 | err = errors.New("Put().ok should have been true") 1611 | b.Fatal(err) 1612 | } 1613 | } 1614 | } 1615 | 1616 | func metaBenchmarkGetByIndexStep(b *testing.B, tree SortedMap, indicesToGet []int) { 1617 | var ( 1618 | err error 1619 | indexToGet int 1620 | ok bool 1621 | ) 1622 | 1623 | for _, indexToGet = range indicesToGet { 1624 | _, _, ok, err = tree.GetByIndex(indexToGet) 1625 | if err != nil { 1626 | err = fmt.Errorf("GetByIndex() returned unexpected error: %v", err) 1627 | b.Fatal(err) 1628 | } 1629 | if !ok { 1630 | err = errors.New("GetByIndex().ok should have been true") 1631 | b.Fatal(err) 1632 | } 1633 | } 1634 | } 1635 | 1636 | func metaBenchmarkPatchByIndexStep(b *testing.B, tree SortedMap, indicesToPatch []int) { 1637 | var ( 1638 | err error 1639 | indexToPatch int 1640 | ok bool 1641 | valueAsString string 1642 | ) 1643 | 1644 | for _, indexToPatch = range indicesToPatch { 1645 | valueAsString = strconv.Itoa(indexToPatch) 1646 | ok, err = tree.PatchByIndex(indexToPatch, valueAsString) 1647 | if err != nil { 1648 | err = fmt.Errorf("PatchByIndex() returned unexpected error: %v", err) 1649 | b.Fatal(err) 1650 | } 1651 | if !ok { 1652 | err = errors.New("PatchByIndex().ok should have been true") 1653 | b.Fatal(err) 1654 | } 1655 | } 1656 | } 1657 | 1658 | func metaBenchmarkDeleteByIndexStep(b *testing.B, tree SortedMap, indicesToDeleteByIndexNormalized []int) { 1659 | var ( 1660 | err error 1661 | indexToDelete int 1662 | ok bool 1663 | ) 1664 | 1665 | for _, indexToDelete = range indicesToDeleteByIndexNormalized { 1666 | ok, err = tree.DeleteByIndex(indexToDelete) 1667 | if err != nil { 1668 | err = fmt.Errorf("DeleteByIndex() returned unexpected error: %v", err) 1669 | b.Fatal(err) 1670 | } 1671 | if !ok { 1672 | err = errors.New("DeleteByIndex().ok should have been true") 1673 | b.Fatal(err) 1674 | } 1675 | } 1676 | } 1677 | 1678 | func metaBenchmarkGetByKeyStep(b *testing.B, tree SortedMap, keysToGet []int) { 1679 | var ( 1680 | err error 1681 | keyToGet int 1682 | ok bool 1683 | ) 1684 | 1685 | for _, keyToGet = range keysToGet { 1686 | _, ok, err = tree.GetByKey(keyToGet) 1687 | if err != nil { 1688 | err = fmt.Errorf("GetByKey() returned unexpected error: %v", err) 1689 | b.Fatal(err) 1690 | } 1691 | if !ok { 1692 | err = errors.New("GetByKey().ok should have been true") 1693 | b.Fatal(err) 1694 | } 1695 | } 1696 | } 1697 | 1698 | func metaBenchmarkBisectLeftStep(b *testing.B, tree SortedMap, keysToBisectLeft []int) { 1699 | var ( 1700 | err error 1701 | found bool 1702 | keyToBisectLeft int 1703 | ) 1704 | 1705 | for _, keyToBisectLeft = range keysToBisectLeft { 1706 | _, found, err = tree.BisectLeft(keyToBisectLeft) 1707 | if err != nil { 1708 | err = fmt.Errorf("BisectLeft() returned unexpected error: %v", err) 1709 | b.Fatal(err) 1710 | } 1711 | if !found { 1712 | err = errors.New("BisectLeft().found should have been true") 1713 | b.Fatal(err) 1714 | } 1715 | } 1716 | } 1717 | 1718 | func metaBenchmarkBisectRightStep(b *testing.B, tree SortedMap, keysToBisectRight []int) { 1719 | var ( 1720 | err error 1721 | found bool 1722 | keyToBisectRight int 1723 | ) 1724 | 1725 | for _, keyToBisectRight = range keysToBisectRight { 1726 | _, found, err = tree.BisectRight(keyToBisectRight) 1727 | if err != nil { 1728 | err = fmt.Errorf("BisectRight() returned unexpected error: %v", err) 1729 | b.Fatal(err) 1730 | } 1731 | if !found { 1732 | err = errors.New("BisectRight().found should have been true") 1733 | b.Fatal(err) 1734 | } 1735 | } 1736 | } 1737 | 1738 | func metaBenchmarkPatchByKeyStep(b *testing.B, tree SortedMap, keysToPatch []int) { 1739 | var ( 1740 | err error 1741 | keyToPatch int 1742 | ok bool 1743 | valueAsString string 1744 | ) 1745 | 1746 | for _, keyToPatch = range keysToPatch { 1747 | valueAsString = strconv.Itoa(keyToPatch) 1748 | ok, err = tree.PatchByKey(keyToPatch, valueAsString) 1749 | if err != nil { 1750 | err = fmt.Errorf("PatchByKey() returned unexpected error: %v", err) 1751 | b.Fatal(err) 1752 | } 1753 | if !ok { 1754 | err = errors.New("PatchByKey().ok should have been true") 1755 | b.Fatal(err) 1756 | } 1757 | } 1758 | } 1759 | 1760 | func metaBenchmarkDeleteByKeyStep(b *testing.B, tree SortedMap, keysToDelete []int) { 1761 | var ( 1762 | err error 1763 | keyToDelete int 1764 | ok bool 1765 | ) 1766 | 1767 | for _, keyToDelete = range keysToDelete { 1768 | ok, err = tree.DeleteByKey(keyToDelete) 1769 | if err != nil { 1770 | err = fmt.Errorf("DeleteByKey() returned unexpected error: %v", err) 1771 | b.Fatal(err) 1772 | } 1773 | if !ok { 1774 | err = errors.New("DeleteByKey().ok should have been true") 1775 | b.Fatal(err) 1776 | } 1777 | } 1778 | } 1779 | 1780 | func metaBenchmarkPut(b *testing.B, tree SortedMap, numKeys int) { 1781 | var ( 1782 | err error 1783 | keysToPut []int 1784 | remainingN int 1785 | ) 1786 | 1787 | keysToPut, err = testKnuthShuffledIntSlice(numKeys) 1788 | if err != nil { 1789 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1790 | b.Fatal(err) 1791 | } 1792 | 1793 | remainingN = b.N 1794 | 1795 | b.ResetTimer() 1796 | 1797 | for remainingN > numKeys { 1798 | metaBenchmarkPutStep(b, tree, keysToPut) 1799 | b.StopTimer() 1800 | remainingN -= numKeys 1801 | metaBenchmarkDeleteByKeyStep(b, tree, keysToPut) 1802 | b.StartTimer() 1803 | } 1804 | metaBenchmarkPutStep(b, tree, keysToPut[:remainingN]) 1805 | } 1806 | 1807 | func metaBenchmarkGetByIndex(b *testing.B, tree SortedMap, numKeys int) { 1808 | var ( 1809 | err error 1810 | indicesToGet []int 1811 | keysToPutForByIndexAPIs []int 1812 | remainingN int 1813 | ) 1814 | 1815 | keysToPutForByIndexAPIs, err = testKnuthShuffledIntSlice(numKeys) 1816 | if err != nil { 1817 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1818 | b.Fatal(err) 1819 | } 1820 | indicesToGet, err = testKnuthShuffledIntSlice(numKeys) 1821 | if err != nil { 1822 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1823 | b.Fatal(err) 1824 | } 1825 | 1826 | metaBenchmarkPutStep(b, tree, keysToPutForByIndexAPIs) 1827 | 1828 | remainingN = b.N 1829 | 1830 | b.ResetTimer() 1831 | 1832 | for remainingN > numKeys { 1833 | metaBenchmarkGetByIndexStep(b, tree, indicesToGet) 1834 | remainingN -= numKeys 1835 | } 1836 | metaBenchmarkGetByIndexStep(b, tree, indicesToGet[:remainingN]) 1837 | } 1838 | 1839 | func metaBenchmarkPatchByIndex(b *testing.B, tree SortedMap, numKeys int) { 1840 | var ( 1841 | err error 1842 | indicesToPatch []int 1843 | keysToPutForByIndexAPIs []int 1844 | remainingN int 1845 | ) 1846 | 1847 | keysToPutForByIndexAPIs, err = testKnuthShuffledIntSlice(numKeys) 1848 | if err != nil { 1849 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1850 | b.Fatal(err) 1851 | } 1852 | indicesToPatch, err = testKnuthShuffledIntSlice(numKeys) 1853 | if err != nil { 1854 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1855 | b.Fatal(err) 1856 | } 1857 | 1858 | metaBenchmarkPutStep(b, tree, keysToPutForByIndexAPIs) 1859 | 1860 | remainingN = b.N 1861 | 1862 | b.ResetTimer() 1863 | 1864 | for remainingN > numKeys { 1865 | metaBenchmarkPatchByIndexStep(b, tree, indicesToPatch) 1866 | remainingN -= numKeys 1867 | } 1868 | metaBenchmarkPatchByIndexStep(b, tree, indicesToPatch[:remainingN]) 1869 | } 1870 | 1871 | func metaBenchmarkDeleteByIndex(b *testing.B, tree SortedMap, numKeys int) { 1872 | var ( 1873 | err error 1874 | indicesToDeleteByIndexNormalized []int 1875 | indicesToDeleteByIndexNotNormalized []int 1876 | keysToPutForByIndexAPIs []int 1877 | remainingN int 1878 | ) 1879 | 1880 | keysToPutForByIndexAPIs, err = testKnuthShuffledIntSlice(numKeys) 1881 | if err != nil { 1882 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1883 | b.Fatal(err) 1884 | } 1885 | indicesToDeleteByIndexNotNormalized, err = testKnuthShuffledIntSlice(numKeys) 1886 | if err != nil { 1887 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1888 | b.Fatal(err) 1889 | } 1890 | indicesToDeleteByIndexNormalized = testFetchIndicesToDeleteNormalized(indicesToDeleteByIndexNotNormalized) 1891 | 1892 | metaBenchmarkPutStep(b, tree, keysToPutForByIndexAPIs) 1893 | 1894 | remainingN = b.N 1895 | 1896 | b.ResetTimer() 1897 | 1898 | for remainingN > numKeys { 1899 | metaBenchmarkDeleteByIndexStep(b, tree, indicesToDeleteByIndexNormalized) 1900 | b.StopTimer() 1901 | remainingN -= numKeys 1902 | metaBenchmarkPutStep(b, tree, keysToPutForByIndexAPIs) 1903 | b.StartTimer() 1904 | } 1905 | metaBenchmarkDeleteByIndexStep(b, tree, indicesToDeleteByIndexNormalized[:remainingN]) 1906 | } 1907 | 1908 | func metaBenchmarkGetByKey(b *testing.B, tree SortedMap, numKeys int) { 1909 | var ( 1910 | err error 1911 | keysToGet []int 1912 | keysToPutForByKeyAPIs []int 1913 | remainingN int 1914 | ) 1915 | 1916 | keysToPutForByKeyAPIs, err = testKnuthShuffledIntSlice(numKeys) 1917 | if err != nil { 1918 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1919 | b.Fatal(err) 1920 | } 1921 | keysToGet, err = testKnuthShuffledIntSlice(numKeys) 1922 | if err != nil { 1923 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1924 | b.Fatal(err) 1925 | } 1926 | 1927 | metaBenchmarkPutStep(b, tree, keysToPutForByKeyAPIs) 1928 | 1929 | remainingN = b.N 1930 | 1931 | b.ResetTimer() 1932 | 1933 | for remainingN > numKeys { 1934 | metaBenchmarkGetByKeyStep(b, tree, keysToGet) 1935 | remainingN -= numKeys 1936 | } 1937 | metaBenchmarkGetByKeyStep(b, tree, keysToGet[:remainingN]) 1938 | } 1939 | 1940 | func metaBenchmarkBisectLeft(b *testing.B, tree SortedMap, numKeys int) { 1941 | var ( 1942 | err error 1943 | keysToBisectLeft []int 1944 | keysToPutForByKeyAPIs []int 1945 | remainingN int 1946 | ) 1947 | 1948 | keysToPutForByKeyAPIs, err = testKnuthShuffledIntSlice(numKeys) 1949 | if err != nil { 1950 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1951 | b.Fatal(err) 1952 | } 1953 | keysToBisectLeft, err = testKnuthShuffledIntSlice(numKeys) 1954 | if err != nil { 1955 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1956 | b.Fatal(err) 1957 | } 1958 | 1959 | metaBenchmarkPutStep(b, tree, keysToPutForByKeyAPIs) 1960 | 1961 | remainingN = b.N 1962 | 1963 | b.ResetTimer() 1964 | 1965 | for remainingN > numKeys { 1966 | metaBenchmarkBisectLeftStep(b, tree, keysToBisectLeft) 1967 | remainingN -= numKeys 1968 | } 1969 | metaBenchmarkBisectLeftStep(b, tree, keysToBisectLeft[:remainingN]) 1970 | } 1971 | 1972 | func metaBenchmarkBisectRight(b *testing.B, tree SortedMap, numKeys int) { 1973 | var ( 1974 | err error 1975 | keysToBisectRight []int 1976 | keysToPutForByKeyAPIs []int 1977 | remainingN int 1978 | ) 1979 | 1980 | keysToPutForByKeyAPIs, err = testKnuthShuffledIntSlice(numKeys) 1981 | if err != nil { 1982 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1983 | b.Fatal(err) 1984 | } 1985 | keysToBisectRight, err = testKnuthShuffledIntSlice(numKeys) 1986 | if err != nil { 1987 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 1988 | b.Fatal(err) 1989 | } 1990 | 1991 | metaBenchmarkPutStep(b, tree, keysToPutForByKeyAPIs) 1992 | 1993 | remainingN = b.N 1994 | 1995 | b.ResetTimer() 1996 | 1997 | for remainingN > numKeys { 1998 | metaBenchmarkBisectRightStep(b, tree, keysToBisectRight) 1999 | remainingN -= numKeys 2000 | } 2001 | metaBenchmarkBisectRightStep(b, tree, keysToBisectRight[:remainingN]) 2002 | } 2003 | 2004 | func metaBenchmarkPatchByKey(b *testing.B, tree SortedMap, numKeys int) { 2005 | var ( 2006 | err error 2007 | keysToPatch []int 2008 | keysToPutForByKeyAPIs []int 2009 | remainingN int 2010 | ) 2011 | 2012 | keysToPutForByKeyAPIs, err = testKnuthShuffledIntSlice(numKeys) 2013 | if err != nil { 2014 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 2015 | b.Fatal(err) 2016 | } 2017 | keysToPatch, err = testKnuthShuffledIntSlice(numKeys) 2018 | if err != nil { 2019 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 2020 | b.Fatal(err) 2021 | } 2022 | 2023 | metaBenchmarkPutStep(b, tree, keysToPutForByKeyAPIs) 2024 | 2025 | remainingN = b.N 2026 | 2027 | b.ResetTimer() 2028 | 2029 | for remainingN > numKeys { 2030 | metaBenchmarkPatchByKeyStep(b, tree, keysToPatch) 2031 | remainingN -= numKeys 2032 | } 2033 | metaBenchmarkPatchByKeyStep(b, tree, keysToPatch[:remainingN]) 2034 | } 2035 | 2036 | func metaBenchmarkDeleteByKey(b *testing.B, tree SortedMap, numKeys int) { 2037 | var ( 2038 | err error 2039 | keysToDelete []int 2040 | keysToPutForByKeyAPIs []int 2041 | remainingN int 2042 | ) 2043 | 2044 | keysToPutForByKeyAPIs, err = testKnuthShuffledIntSlice(numKeys) 2045 | if err != nil { 2046 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 2047 | b.Fatal(err) 2048 | } 2049 | keysToDelete, err = testKnuthShuffledIntSlice(numKeys) 2050 | if err != nil { 2051 | err = fmt.Errorf("Knuth Shuffle failed: %v", err) 2052 | b.Fatal(err) 2053 | } 2054 | 2055 | metaBenchmarkPutStep(b, tree, keysToPutForByKeyAPIs) 2056 | 2057 | remainingN = b.N 2058 | 2059 | b.ResetTimer() 2060 | 2061 | for remainingN > numKeys { 2062 | metaBenchmarkDeleteByKeyStep(b, tree, keysToDelete) 2063 | b.StopTimer() 2064 | remainingN -= numKeys 2065 | metaBenchmarkPutStep(b, tree, keysToPutForByKeyAPIs) 2066 | b.StartTimer() 2067 | } 2068 | metaBenchmarkDeleteByKeyStep(b, tree, keysToDelete[:remainingN]) 2069 | } 2070 | -------------------------------------------------------------------------------- /compare_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "testing" 8 | "time" 9 | ) 10 | 11 | func TestCompareFunctions(t *testing.T) { 12 | var ( 13 | result int 14 | err error 15 | ) 16 | 17 | result, err = CompareInt(int(1), int(2)) 18 | if err != nil { 19 | t.Fatalf("CompareInt(int(1),int(2)) should not have failed") 20 | } 21 | if result >= 0 { 22 | t.Fatalf("CompareInt(int(1),int(2)) should have been < 0") 23 | } 24 | result, err = CompareInt(int(2), int(2)) 25 | if err != nil { 26 | t.Fatalf("CompareInt(int(2),int(2)) should not have failed") 27 | } 28 | if result != 0 { 29 | t.Fatalf("CompareInt(int(2),int(2)) should have been == 0") 30 | } 31 | result, err = CompareInt(int(3), int(2)) 32 | if err != nil { 33 | t.Fatalf("CompareInt(int(3),int(2)) should not have failed") 34 | } 35 | if result <= 0 { 36 | t.Fatalf("CompareInt(int(3),int(2)) should have been > 0") 37 | } 38 | _, err = CompareInt(int(2), uint32(2)) 39 | if err == nil { 40 | t.Fatalf("CompareInt(int(2),uint32(2)) should have failed") 41 | } 42 | _, err = CompareInt(uint32(2), int(2)) 43 | if err == nil { 44 | t.Fatalf("CompareInt(uint32(2),int(2)) should have failed") 45 | } 46 | 47 | result, err = CompareUint16(uint16(1), uint16(2)) 48 | if err != nil { 49 | t.Fatalf("CompareUint16(uint16(1),uint16(2)) should not have failed") 50 | } 51 | if result >= 0 { 52 | t.Fatalf("CompareUint16(uint32(1),uint32(2)) should have been < 0") 53 | } 54 | result, err = CompareUint16(uint16(2), uint16(2)) 55 | if err != nil { 56 | t.Fatalf("CompareUint16(uint16(2),uint16(2)) should not have failed") 57 | } 58 | if result != 0 { 59 | t.Fatalf("CompareUint16(uint16(2),uint16(2)) should have been == 0") 60 | } 61 | result, err = CompareUint16(uint16(3), uint16(2)) 62 | if err != nil { 63 | t.Fatalf("CompareUint16(uint16(3),uint16(2)) should not have failed") 64 | } 65 | if result <= 0 { 66 | t.Fatalf("CompareUint16(uint16(3),uint16(2)) should have been > 0") 67 | } 68 | _, err = CompareUint16(uint32(2), uint64(2)) 69 | if err == nil { 70 | t.Fatalf("CompareUint16(uint16(2), uint32(2)) should have failed") 71 | } 72 | _, err = CompareUint16(uint64(2), uint32(2)) 73 | if err == nil { 74 | t.Fatalf("CompareUint16(uint32(2), uint16(2)) should have failed") 75 | } 76 | 77 | result, err = CompareUint32(uint32(1), uint32(2)) 78 | if err != nil { 79 | t.Fatalf("CompareUint32(uint32(1),uint32(2)) should not have failed") 80 | } 81 | if result >= 0 { 82 | t.Fatalf("CompareUint32(uint32(1),uint32(2)) should have been < 0") 83 | } 84 | result, err = CompareUint32(uint32(2), uint32(2)) 85 | if err != nil { 86 | t.Fatalf("CompareUint32(uint32(2),uint32(2)) should not have failed") 87 | } 88 | if result != 0 { 89 | t.Fatalf("CompareUint32(uint32(2),uint32(2)) should have been == 0") 90 | } 91 | result, err = CompareUint32(uint32(3), uint32(2)) 92 | if err != nil { 93 | t.Fatalf("CompareUint32(uint32(3),uint32(2)) should not have failed") 94 | } 95 | if result <= 0 { 96 | t.Fatalf("CompareUint32(uint32(3),uint32(2)) should have been > 0") 97 | } 98 | _, err = CompareUint32(uint32(2), uint64(2)) 99 | if err == nil { 100 | t.Fatalf("CompareUint32(uint32(2), uint64(2)) should have failed") 101 | } 102 | _, err = CompareUint32(uint64(2), uint32(2)) 103 | if err == nil { 104 | t.Fatalf("CompareUint32(uint64(2), uint32(2)) should have failed") 105 | } 106 | 107 | result, err = CompareUint64(uint64(1), uint64(2)) 108 | if err != nil { 109 | t.Fatalf("CompareUint64(uint32(1),uint32(2)) should not have failed") 110 | } 111 | if result >= 0 { 112 | t.Fatalf("CompareUint64(uint64(1),uint64(2)) should have been < 0") 113 | } 114 | result, err = CompareUint64(uint64(2), uint64(2)) 115 | if err != nil { 116 | t.Fatalf("CompareUint64(uint32(2),uint32(2)) should not have failed") 117 | } 118 | if result != 0 { 119 | t.Fatalf("CompareUint64(uint64(2),uint64(2)) should have been == 0") 120 | } 121 | result, err = CompareUint64(uint64(3), uint64(2)) 122 | if err != nil { 123 | t.Fatalf("CompareUint64(uint32(3),uint32(2)) should not have failed") 124 | } 125 | if result <= 0 { 126 | t.Fatalf("CompareUint64(uint64(3),uint64(2)) should have been > 0") 127 | } 128 | _, err = CompareUint64(uint64(2), string("2")) 129 | if err == nil { 130 | t.Fatalf("CompareUint64(uint64(2), string(\"2\")) should have failed") 131 | } 132 | _, err = CompareUint64(string("2"), uint64(2)) 133 | if err == nil { 134 | t.Fatalf("CompareUint64(string(\"2\"), uint64(2)) should have failed") 135 | } 136 | 137 | result, err = CompareTime(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC)) 138 | if err != nil { 139 | t.Fatalf("CompareTime(time.Date(2000,1,1,0,0,0,0,time.UTC),time.Date(3000,1,1,0,0,0,0,time.UTC)) should not have failed") 140 | } 141 | if result >= 0 { 142 | t.Fatalf("CompareTime(time.Date(2000,1,1,0,0,0,0,time.UTC),time.Date(3000,1,1,0,0,0,0,time.UTC)) should have been < 0") 143 | } 144 | result, err = CompareTime(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) 145 | if err != nil { 146 | t.Fatalf("CompareTime(time.Date(2000,1,1,0,0,0,0,time.UTC),time.Date(2000,1,1,0,0,0,0,time.UTC)) should not have failed") 147 | } 148 | if result != 0 { 149 | t.Fatalf("CompareTime(time.Date(2000,1,1,0,0,0,0,time.UTC),time.Date(2000,1,1,0,0,0,0,time.UTC)) should have been == 0") 150 | } 151 | result, err = CompareTime(time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC), time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC)) 152 | if err != nil { 153 | t.Fatalf("CompareTime(time.Date(3000,1,1,0,0,0,0,time.UTC),time.Date(2000,1,1,0,0,0,0,time.UTC)) should not have failed") 154 | } 155 | if result <= 0 { 156 | t.Fatalf("CompareTime(time.Date(3000,1,1,0,0,0,0,time.UTC),time.Date(2000,1,1,0,0,0,0,time.UTC)) should have been > 0") 157 | } 158 | _, err = CompareTime(time.Date(2000, 1, 1, 0, 0, 0, 0, time.UTC), string("not a time.Time")) 159 | if err == nil { 160 | t.Fatalf("CompareTime(time.Date(2000,1,1,0,0,0,0,time.UTC),string(\"not a time.Time\")) should have failed") 161 | } 162 | _, err = CompareTime(string("not a time.Time"), time.Date(3000, 1, 1, 0, 0, 0, 0, time.UTC)) 163 | if err == nil { 164 | t.Fatalf("CompareTime(string(\"not a time.Time\"),time.Date(3000,1,1,0,0,0,0,time.UTC)) should have failed") 165 | } 166 | 167 | result, err = CompareString(string("1"), string("2")) 168 | if err != nil { 169 | t.Fatalf("CompareString(string(\"1\"), string(\"2\")) should not have failed") 170 | } 171 | if result >= 0 { 172 | t.Fatalf("CompareString(string(\"1\"), string(\"2\")) should have been < 0") 173 | } 174 | result, err = CompareString(string("2"), string("2")) 175 | if err != nil { 176 | t.Fatalf("CompareString(string(\"2\"), string(\"2\")) should not have failed") 177 | } 178 | if result != 0 { 179 | t.Fatalf("CompareString(string(\"2\"), string(\"2\")) should have been == 0") 180 | } 181 | result, err = CompareString(string("3"), string("2")) 182 | if err != nil { 183 | t.Fatalf("CompareString(string(\"3\"), string(\"2\")) should not have failed") 184 | } 185 | if result <= 0 { 186 | t.Fatalf("CompareString(string(\"3\"), string(\"2\")) should have been > 0") 187 | } 188 | _, err = CompareString(string("2"), []byte{2}) 189 | if err == nil { 190 | t.Fatalf("CompareString(string(\"2\"), []byte{2}) should have failed") 191 | } 192 | _, err = CompareString([]byte{2}, string("2")) 193 | if err == nil { 194 | t.Fatalf("CompareString([]byte{2}, string(\"2\")) should have failed") 195 | } 196 | 197 | result, err = CompareByteSlice([]byte{1}, []byte{2}) 198 | if err != nil { 199 | t.Fatalf("CompareByteSlice([]byte{1}, []byte{2}) should not have failed") 200 | } 201 | if result >= 0 { 202 | t.Fatalf("CompareByteSlice([]byte{1}, []byte{2}) should have been < 0") 203 | } 204 | result, err = CompareByteSlice([]byte{2}, []byte{2}) 205 | if err != nil { 206 | t.Fatalf("CompareByteSlice([]byte{2}, []byte{2}) should not have failed") 207 | } 208 | if result != 0 { 209 | t.Fatalf("CompareByteSlice([]byte{2}, []byte{2}) should have been == 0") 210 | } 211 | result, err = CompareByteSlice([]byte{3}, []byte{2}) 212 | if err != nil { 213 | t.Fatalf("CompareByteSlice([]byte{3}, []byte{2}) should not have failed") 214 | } 215 | if result <= 0 { 216 | t.Fatalf("CompareByteSlice([]byte{3}, []byte{2}) should have been > 0") 217 | } 218 | _, err = CompareByteSlice([]byte{2}, int(2)) 219 | if err == nil { 220 | t.Fatalf("CompareByteSlice([]byte{2}, int(2)) should have failed") 221 | } 222 | 223 | _, err = CompareByteSlice(int(2), []byte{2}) 224 | if err == nil { 225 | t.Fatalf("CompareByteSlice(int(2), []byte{2}) should have failed") 226 | } 227 | } 228 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/NVIDIA/sortedmap 2 | 3 | go 1.23 4 | 5 | toolchain go1.23.5 6 | 7 | require github.com/NVIDIA/cstruct v0.0.0-20250307070147-f45bd3a10b69 8 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/NVIDIA/cstruct v0.0.0-20250307070147-f45bd3a10b69 h1:08XdgOXVu9khzxiXCoO4M4OMDMhxY9KkLpO6TamzPgs= 2 | github.com/NVIDIA/cstruct v0.0.0-20250307070147-f45bd3a10b69/go.mod h1:Vjlom704MubZmUijKBqJt0yONPC4355FTjPS72GYCio= 3 | -------------------------------------------------------------------------------- /llrb_common_api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "errors" 8 | "strconv" 9 | "testing" 10 | ) 11 | 12 | const ( 13 | commonLLRBTreeBenchmarkNumKeys = 1000 14 | ) 15 | 16 | type commonLLRBTreeTestContextStruct struct { 17 | t *testing.T 18 | tree LLRBTree 19 | } 20 | 21 | func (context *commonLLRBTreeTestContextStruct) DumpKey(key Key) (keyAsString string, err error) { 22 | keyAsInt, ok := key.(int) 23 | if !ok { 24 | context.t.Fatalf("DumpKey() argument not an int") 25 | } 26 | keyAsString = strconv.Itoa(keyAsInt) 27 | err = nil 28 | return 29 | } 30 | 31 | func (context *commonLLRBTreeTestContextStruct) DumpValue(value Value) (valueAsString string, err error) { 32 | valueAsString, ok := value.(string) 33 | if !ok { 34 | context.t.Fatalf("PackValue() argument not a string") 35 | } 36 | err = nil 37 | return 38 | } 39 | 40 | type commonLLRBTreeBenchmarkContextStruct struct { 41 | b *testing.B 42 | tree LLRBTree 43 | } 44 | 45 | func (*commonLLRBTreeBenchmarkContextStruct) DumpKey(_ Key) (keyAsString string, err error) { 46 | err = errors.New("DumpKey() not implemented") 47 | return 48 | } 49 | 50 | func (*commonLLRBTreeBenchmarkContextStruct) DumpValue(_ Value) (valueAsString string, err error) { 51 | err = errors.New("DumpValue() not implemented") 52 | return 53 | } 54 | 55 | func TestLLRBTreeAllButDeleteSimple(t *testing.T) { 56 | context := &commonLLRBTreeTestContextStruct{t: t} 57 | context.tree = NewLLRBTree(CompareInt, context) 58 | metaTestAllButDeleteSimple(t, context.tree) 59 | } 60 | 61 | func TestLLRBTreeDeleteByIndexSimple(t *testing.T) { 62 | context := &commonLLRBTreeTestContextStruct{t: t} 63 | context.tree = NewLLRBTree(CompareInt, context) 64 | metaTestDeleteByIndexSimple(t, context.tree) 65 | } 66 | 67 | func TestLLRBTreeDeleteByKeySimple(t *testing.T) { 68 | context := &commonLLRBTreeTestContextStruct{t: t} 69 | context.tree = NewLLRBTree(CompareInt, context) 70 | metaTestDeleteByKeySimple(t, context.tree) 71 | } 72 | 73 | func TestLLRBTreeInsertGetDeleteByIndexTrivial(t *testing.T) { 74 | context := &commonLLRBTreeTestContextStruct{t: t} 75 | context.tree = NewLLRBTree(CompareInt, context) 76 | metaTestInsertGetDeleteByIndexTrivial(t, context.tree) 77 | } 78 | 79 | func TestLLRBTreeInsertGetDeleteByIndexSmall(t *testing.T) { 80 | context := &commonLLRBTreeTestContextStruct{t: t} 81 | context.tree = NewLLRBTree(CompareInt, context) 82 | metaTestInsertGetDeleteByIndexSmall(t, context.tree) 83 | } 84 | 85 | func TestLLRBTreeInsertGetDeleteByIndexLarge(t *testing.T) { 86 | context := &commonLLRBTreeTestContextStruct{t: t} 87 | context.tree = NewLLRBTree(CompareInt, context) 88 | metaTestInsertGetDeleteByIndexLarge(t, context.tree) 89 | } 90 | 91 | func TestLLRBTreeInsertGetDeleteByIndexHuge(t *testing.T) { 92 | context := &commonLLRBTreeTestContextStruct{t: t} 93 | context.tree = NewLLRBTree(CompareInt, context) 94 | metaTestInsertGetDeleteByIndexHuge(t, context.tree) 95 | } 96 | 97 | func TestLLRBTreeInsertGetDeleteByKeyTrivial(t *testing.T) { 98 | context := &commonLLRBTreeTestContextStruct{t: t} 99 | context.tree = NewLLRBTree(CompareInt, context) 100 | metaTestInsertGetDeleteByKeyTrivial(t, context.tree) 101 | } 102 | 103 | func TestLLRBTreeInsertGetDeleteByKeySmall(t *testing.T) { 104 | context := &commonLLRBTreeTestContextStruct{t: t} 105 | context.tree = NewLLRBTree(CompareInt, context) 106 | metaTestInsertGetDeleteByKeySmall(t, context.tree) 107 | } 108 | 109 | func TestLLRBTreeInsertGetDeleteByKeyLarge(t *testing.T) { 110 | context := &commonLLRBTreeTestContextStruct{t: t} 111 | context.tree = NewLLRBTree(CompareInt, context) 112 | metaTestInsertGetDeleteByKeyLarge(t, context.tree) 113 | } 114 | 115 | func TestLLRBTreeInsertGetDeleteByKeyHuge(t *testing.T) { 116 | context := &commonLLRBTreeTestContextStruct{t: t} 117 | context.tree = NewLLRBTree(CompareInt, context) 118 | metaTestInsertGetDeleteByKeyHuge(t, context.tree) 119 | } 120 | 121 | func TestLLRBTreeBisect(t *testing.T) { 122 | context := &commonLLRBTreeTestContextStruct{t: t} 123 | context.tree = NewLLRBTree(CompareInt, context) 124 | metaTestBisect(t, context.tree) 125 | } 126 | 127 | func BenchmarkLLRBTreePut(b *testing.B) { 128 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 129 | context.tree = NewLLRBTree(CompareInt, context) 130 | metaBenchmarkPut(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 131 | } 132 | 133 | func BenchmarkLLRBTreeGetByIndex(b *testing.B) { 134 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 135 | context.tree = NewLLRBTree(CompareInt, context) 136 | metaBenchmarkGetByIndex(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 137 | } 138 | 139 | func BenchmarkLLRBTreePatchByIndex(b *testing.B) { 140 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 141 | context.tree = NewLLRBTree(CompareInt, context) 142 | metaBenchmarkPatchByIndex(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 143 | } 144 | 145 | func BenchmarkLLRBTreeDeleteByIndex(b *testing.B) { 146 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 147 | context.tree = NewLLRBTree(CompareInt, context) 148 | metaBenchmarkDeleteByIndex(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 149 | } 150 | 151 | func BenchmarkLLRBTreeGetByKey(b *testing.B) { 152 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 153 | context.tree = NewLLRBTree(CompareInt, context) 154 | metaBenchmarkGetByKey(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 155 | } 156 | 157 | func BenchmarkLLRBTreeBisectLeft(b *testing.B) { 158 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 159 | context.tree = NewLLRBTree(CompareInt, context) 160 | metaBenchmarkBisectLeft(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 161 | } 162 | 163 | func BenchmarkLLRBTreeBisectRight(b *testing.B) { 164 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 165 | context.tree = NewLLRBTree(CompareInt, context) 166 | metaBenchmarkBisectRight(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 167 | } 168 | 169 | func BenchmarkLLRBTreePatchByKey(b *testing.B) { 170 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 171 | context.tree = NewLLRBTree(CompareInt, context) 172 | metaBenchmarkPatchByKey(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 173 | } 174 | 175 | func BenchmarkLLRBTreeDeleteByKey(b *testing.B) { 176 | context := &commonLLRBTreeBenchmarkContextStruct{b: b} 177 | context.tree = NewLLRBTree(CompareInt, context) 178 | metaBenchmarkDeleteByKey(b, context.tree, commonLLRBTreeBenchmarkNumKeys) 179 | } 180 | -------------------------------------------------------------------------------- /llrb_dump.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | func (tree *llrbTreeStruct) Dump() (err error) { 11 | tree.Lock() 12 | defer tree.Unlock() 13 | 14 | err = nil 15 | 16 | err = tree.dumpInFlatForm(tree.root) 17 | if err != nil { 18 | err = fmt.Errorf("dumpInFlatForm() failed: %v", err) 19 | fmt.Printf("\n%v\n", err) 20 | return 21 | } 22 | 23 | err = tree.dumpInTreeForm() 24 | if err != nil { 25 | err = fmt.Errorf("dumpInTreeForm() failed: %v", err) 26 | fmt.Printf("\n%v\n", err) 27 | return 28 | } 29 | 30 | err = nil 31 | return 32 | } 33 | 34 | func (tree *llrbTreeStruct) dumpInFlatForm(node *llrbNodeStruct) (err error) { 35 | if node == nil { 36 | err = nil 37 | return 38 | } 39 | 40 | nodeLeftKey := "nil" 41 | if node.left != nil { 42 | nodeLeftKey, err = tree.DumpKey(node.left.Key) 43 | if err != nil { 44 | return 45 | } 46 | } 47 | 48 | nodeRightKey := "nil" 49 | if node.right != nil { 50 | nodeRightKey, err = tree.DumpKey(node.right.Key) 51 | if err != nil { 52 | return 53 | } 54 | } 55 | 56 | var colorString string 57 | // if RED == node.color { 58 | if node.color { 59 | colorString = "RED" 60 | } else { // BLACK == node.color 61 | colorString = "BLACK" 62 | } 63 | 64 | nodeThisKey, err := tree.DumpKey(node.Key) 65 | if err != nil { 66 | return 67 | } 68 | 69 | fmt.Printf("%v Node Key == %v Node.left.Key == %v Node.right.Key == %v len == %v\n", colorString, nodeThisKey, nodeLeftKey, nodeRightKey, node.len) 70 | 71 | err = tree.dumpInFlatForm(node.left) 72 | if err != nil { 73 | return 74 | } 75 | 76 | err = tree.dumpInFlatForm(node.right) 77 | if err != nil { 78 | return 79 | } 80 | 81 | err = nil 82 | return 83 | } 84 | 85 | func (tree *llrbTreeStruct) dumpInTreeForm() (err error) { 86 | if tree.root == nil { 87 | err = nil 88 | return 89 | } 90 | 91 | if tree.root.right != nil { 92 | err = tree.dumpInTreeFormNode(tree.root.right, true, "") 93 | if err != nil { 94 | return 95 | } 96 | } 97 | 98 | rootKey, err := tree.DumpKey(tree.root.Key) 99 | if err != nil { 100 | return 101 | } 102 | 103 | fmt.Printf("%v\n", rootKey) 104 | 105 | if tree.root.left != nil { 106 | err = tree.dumpInTreeFormNode(tree.root.left, false, "") 107 | if err != nil { 108 | return 109 | } 110 | } 111 | 112 | err = nil 113 | return 114 | } 115 | 116 | func (tree *llrbTreeStruct) dumpInTreeFormNode(node *llrbNodeStruct, isRight bool, indent string) (err error) { 117 | var indentAppendage string 118 | var nextIndent string 119 | 120 | if node.right != nil { 121 | if isRight { 122 | indentAppendage = " " 123 | } else { 124 | indentAppendage = " | " 125 | } 126 | nextIndent = indent + indentAppendage 127 | err = tree.dumpInTreeFormNode(node.right, true, nextIndent) 128 | if err != nil { 129 | return 130 | } 131 | } 132 | 133 | fmt.Printf("%v", indent) 134 | if isRight { 135 | fmt.Printf(" /") 136 | } else { 137 | fmt.Printf(" \\") 138 | } 139 | 140 | nodeKey, err := tree.DumpKey(node.Key) 141 | if err != nil { 142 | return 143 | } 144 | 145 | fmt.Printf("----- %v\n", nodeKey) 146 | 147 | if node.left != nil { 148 | if isRight { 149 | indentAppendage = " | " 150 | } else { 151 | indentAppendage = " " 152 | } 153 | nextIndent = indent + indentAppendage 154 | err = tree.dumpInTreeFormNode(node.left, false, nextIndent) 155 | if err != nil { 156 | return 157 | } 158 | } 159 | 160 | err = nil 161 | return 162 | } 163 | -------------------------------------------------------------------------------- /llrb_specific_api_test.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import ( 7 | "testing" 8 | ) 9 | 10 | func TestLLRBTreeReset(t *testing.T) { 11 | context := &commonLLRBTreeTestContextStruct{t: t} 12 | context.tree = NewLLRBTree(CompareInt, context) 13 | context.tree.Reset() 14 | } 15 | -------------------------------------------------------------------------------- /llrb_tree.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | // Left-Leaning Red-Black (LLRB) described here: 7 | // 8 | // http://www.cs.princeton.edu/~rs/talks/LLRB/08Penn.pdf 9 | // http://www.cs.princeton.edu/~rs/talks/LLRB/LLRB.pdf 10 | // http://www.cs.princeton.edu/~rs/talks/LLRB/Java/RedBlackBST.java 11 | // 12 | // Red-Black Trees are implemented by "coloring" nodes of a Binary Search Tree ("BST") 13 | // so as to map a 2-3-4 Tree isomorphically. A 2-3 Tree may also be used with this 14 | // same coloring scheme and will simplify the algorithms even more (with only a negligible 15 | // performance impact). The implementation herein utilizes this 2-3 Tree basis. 16 | 17 | import "sync" 18 | 19 | const ( 20 | RED = true 21 | BLACK = false 22 | ) 23 | 24 | type llrbNodeStruct struct { 25 | Key 26 | Value 27 | left *llrbNodeStruct // Pointer to Left Child (or nil) 28 | right *llrbNodeStruct // Pointer to Right Child (or nil) 29 | color bool // Color of parent link 30 | len int // Number of nodes (including this node) in a tree "rooted" by this node 31 | } 32 | 33 | type llrbTreeStruct struct { 34 | sync.Mutex 35 | Compare 36 | LLRBTreeCallbacks 37 | root *llrbNodeStruct 38 | } 39 | 40 | // API functions (see api.go) 41 | 42 | func (tree *llrbTreeStruct) BisectLeft(key Key) (index int, found bool, err error) { 43 | tree.Lock() 44 | defer tree.Unlock() 45 | 46 | node := tree.root 47 | 48 | if node == nil { 49 | index = -1 50 | found = false 51 | err = nil 52 | 53 | return 54 | } 55 | 56 | nodeIndex := 0 // computed index of current node 57 | if node.left != nil { 58 | nodeIndex = node.left.len 59 | } 60 | 61 | compareResult, compareErr := tree.Compare(key, node.Key) // Pre-load recursion test value 62 | if compareErr != nil { 63 | err = compareErr 64 | return 65 | } 66 | 67 | for compareResult != 0 { 68 | if compareResult < 0 { // key < node.Key 69 | node = node.left 70 | 71 | if node == nil { 72 | // key not found, nodeIndex points to key:value just after where key would go 73 | 74 | index = nodeIndex - 1 75 | found = false 76 | err = nil 77 | 78 | return 79 | } 80 | 81 | // nil != node, so recurse from here 82 | 83 | if node.right == nil { 84 | nodeIndex-- 85 | } else { // node.right != nil 86 | nodeIndex -= node.right.len + 1 87 | } 88 | } else { // compareResult > 0 (key > node.Key) 89 | node = node.right 90 | 91 | if node == nil { 92 | // key not found, nodeIndex points to key:value just before where key would go 93 | 94 | index = nodeIndex 95 | found = false 96 | err = nil 97 | 98 | return 99 | } 100 | 101 | // nil != node, so recurse from here 102 | 103 | if node.left == nil { 104 | nodeIndex++ 105 | } else { // node.left != nil 106 | nodeIndex += node.left.len + 1 107 | } 108 | } 109 | 110 | compareResult, compareErr = tree.Compare(key, node.Key) // next recursion step's test value 111 | if compareErr != nil { 112 | err = compareErr 113 | return 114 | } 115 | } 116 | 117 | // If we reach here, nodeIndex is to matching key 118 | 119 | index = nodeIndex 120 | found = true 121 | err = nil 122 | 123 | return 124 | } 125 | 126 | func (tree *llrbTreeStruct) BisectRight(key Key) (index int, found bool, err error) { 127 | tree.Lock() 128 | defer tree.Unlock() 129 | 130 | node := tree.root 131 | 132 | if node == nil { 133 | index = 0 134 | found = false 135 | err = nil 136 | 137 | return 138 | } 139 | 140 | nodeIndex := 0 // computed index of current node 141 | if node.left != nil { 142 | nodeIndex = node.left.len 143 | } 144 | 145 | compareResult, compareErr := tree.Compare(key, node.Key) // Pre-load recursion test value 146 | if compareErr != nil { 147 | err = compareErr 148 | return 149 | } 150 | 151 | for compareResult != 0 { 152 | if compareResult < 0 { // key < node.Key 153 | node = node.left 154 | 155 | if node == nil { 156 | // key not found, nodeIndex points to key:value just after where key would go 157 | 158 | index = nodeIndex 159 | found = false 160 | err = nil 161 | 162 | return 163 | } 164 | 165 | // node != nil, so recurse from here 166 | 167 | if node.right == nil { 168 | nodeIndex-- 169 | } else { // node.right != nil 170 | nodeIndex -= node.right.len + 1 171 | } 172 | } else { // compareResult > 0 (key > node.Key) 173 | node = node.right 174 | 175 | if node == nil { 176 | // key not found, nodeIndex points to key:value just before where key would go 177 | 178 | index = nodeIndex + 1 179 | found = false 180 | err = nil 181 | 182 | return 183 | } 184 | 185 | // node != nil, so recurse from here 186 | 187 | if node.left == nil { 188 | nodeIndex++ 189 | } else { // node.left != nil 190 | nodeIndex += node.left.len + 1 191 | } 192 | } 193 | 194 | compareResult, compareErr = tree.Compare(key, node.Key) // next recursion step's test value 195 | if compareErr != nil { 196 | err = compareErr 197 | return 198 | } 199 | } 200 | 201 | // If we reach here, nodeIndex is to matching key 202 | 203 | index = nodeIndex 204 | found = true 205 | err = nil 206 | 207 | return 208 | } 209 | 210 | func (tree *llrbTreeStruct) DeleteByIndex(index int) (ok bool, err error) { 211 | tree.Lock() 212 | defer tree.Unlock() 213 | 214 | if (index < 0) || (tree.root == nil) || (index >= tree.root.len) { 215 | ok = false 216 | err = nil 217 | 218 | return 219 | } 220 | 221 | ok = true // index is within [0,# nodes), so we know we will succeed 222 | 223 | key := tree.preDeleteByIndexAdjustLen(tree.root, index) 224 | 225 | tree.root, err = tree.delete(tree.root, key) 226 | if err != nil { 227 | return 228 | } 229 | 230 | if tree.root != nil { 231 | tree.root.color = BLACK 232 | } 233 | 234 | err = nil 235 | 236 | return 237 | } 238 | 239 | func (tree *llrbTreeStruct) DeleteByKey(key Key) (ok bool, err error) { 240 | tree.Lock() 241 | defer tree.Unlock() 242 | 243 | ok, err = tree.preDeleteByKeyAdjustLen(tree.root, key) 244 | if err != nil { 245 | return 246 | } 247 | if !ok { 248 | err = nil 249 | return 250 | } 251 | 252 | tree.root, err = tree.delete(tree.root, key) 253 | if err != nil { 254 | return 255 | } 256 | if tree.root != nil { 257 | tree.root.color = BLACK 258 | } 259 | 260 | return 261 | } 262 | 263 | func (tree *llrbTreeStruct) GetByIndex(index int) (key Key, value Value, ok bool, err error) { 264 | tree.Lock() 265 | defer tree.Unlock() 266 | 267 | err = nil 268 | 269 | node := tree.root 270 | 271 | if (index < 0) || (node == nil) || (index >= node.len) { 272 | key = nil 273 | value = nil 274 | ok = false 275 | 276 | return 277 | } 278 | 279 | ok = true // index is within [0,# nodes), so we know we will succeed 280 | 281 | nodeIndex := 0 // computed index of current node 282 | if node.left != nil { 283 | nodeIndex = node.left.len 284 | } 285 | 286 | for nodeIndex != index { 287 | if nodeIndex > index { 288 | node = node.left 289 | 290 | if node.right == nil { 291 | nodeIndex-- 292 | } else { // node.right != nil 293 | nodeIndex -= node.right.len + 1 294 | } 295 | } else { // nodeIndex < index 296 | node = node.right 297 | 298 | if node.left == nil { 299 | nodeIndex++ 300 | } else { // node.left != nil 301 | nodeIndex += node.left.len + 1 302 | } 303 | } 304 | } 305 | 306 | key = node.Key 307 | value = node.Value 308 | 309 | return 310 | } 311 | 312 | func (tree *llrbTreeStruct) GetByKey(key Key) (value Value, ok bool, err error) { 313 | tree.Lock() 314 | defer tree.Unlock() 315 | 316 | node := tree.root 317 | 318 | for node != nil { 319 | compareResult, compareErr := tree.Compare(key, node.Key) 320 | if compareErr != nil { 321 | err = compareErr 322 | return 323 | } 324 | 325 | switch { 326 | case compareResult < 0: // key < node.Key 327 | node = node.left 328 | case compareResult > 0: // key > node.Key 329 | node = node.right 330 | default: // compareResult == 0 (key == node.Key) 331 | value = node.Value 332 | ok = true 333 | err = nil 334 | 335 | return 336 | } 337 | } 338 | 339 | // If we reach here, key was not found 340 | 341 | value = nil 342 | ok = false 343 | err = nil 344 | 345 | return 346 | } 347 | 348 | func (tree *llrbTreeStruct) Len() (numberOfItems int, err error) { 349 | tree.Lock() 350 | defer tree.Unlock() 351 | 352 | err = nil 353 | 354 | if tree.root == nil { 355 | numberOfItems = 0 356 | } else { 357 | numberOfItems = tree.root.len 358 | } 359 | 360 | return 361 | } 362 | 363 | func (tree *llrbTreeStruct) PatchByIndex(index int, value Value) (ok bool, err error) { 364 | tree.Lock() 365 | defer tree.Unlock() 366 | 367 | err = nil 368 | 369 | node := tree.root 370 | 371 | if (index < 0) || (node == nil) || (index >= node.len) { 372 | ok = false 373 | 374 | return 375 | } 376 | 377 | ok = true // index is within [0,# nodes), so we know we will succeed 378 | 379 | nodeIndex := 0 // computed index of current node 380 | if node.left != nil { 381 | nodeIndex = node.left.len 382 | } 383 | 384 | for nodeIndex != index { 385 | if nodeIndex > index { 386 | node = node.left 387 | 388 | if node.right == nil { 389 | nodeIndex-- 390 | } else { // node.right != nil 391 | nodeIndex -= node.right.len + 1 392 | } 393 | } else { // nodeIndex < index 394 | node = node.right 395 | 396 | if node.left == nil { 397 | nodeIndex++ 398 | } else { // node.left != nil 399 | nodeIndex += node.left.len + 1 400 | } 401 | } 402 | } 403 | 404 | node.Value = value 405 | 406 | return 407 | } 408 | 409 | func (tree *llrbTreeStruct) PatchByKey(key Key, value Value) (ok bool, err error) { 410 | tree.Lock() 411 | defer tree.Unlock() 412 | 413 | node := tree.root 414 | 415 | for node != nil { 416 | compareResult, compareErr := tree.Compare(key, node.Key) 417 | if compareErr != nil { 418 | err = compareErr 419 | return 420 | } 421 | 422 | switch { 423 | case compareResult < 0: // key < node.Key 424 | node = node.left 425 | case compareResult > 0: // key > node.Key 426 | node = node.right 427 | default: // compareResult == 0 (key == node.Key) 428 | node.Value = value 429 | ok = true 430 | err = nil 431 | 432 | return 433 | } 434 | } 435 | 436 | // If we reach here, key was not found 437 | 438 | ok = false 439 | err = nil 440 | 441 | return 442 | } 443 | 444 | func (tree *llrbTreeStruct) Put(key Key, value Value) (ok bool, err error) { 445 | tree.Lock() 446 | defer tree.Unlock() 447 | 448 | updatedRoot, ok, err := tree.insert(tree.root, key, value) 449 | if err != nil { 450 | return 451 | } 452 | 453 | if ok { 454 | tree.root = updatedRoot 455 | tree.postInsertAdjustLen(tree.root, key) 456 | tree.root.color = BLACK 457 | } 458 | 459 | err = nil 460 | 461 | return 462 | } 463 | 464 | func (tree *llrbTreeStruct) Reset() { 465 | tree.root = nil 466 | } 467 | 468 | // Recursive functions 469 | 470 | func (tree *llrbTreeStruct) insert(oldNexusNode *llrbNodeStruct, key Key, value Value) (newNexusNode *llrbNodeStruct, ok bool, err error) { 471 | if oldNexusNode == nil { 472 | // Add new leaf node - .len will be updated by later call to postInsertAdjustLen() 473 | 474 | newNexusNode = &llrbNodeStruct{Key: key, Value: value, left: nil, right: nil, color: RED, len: 0} 475 | ok = true 476 | err = nil 477 | 478 | return 479 | } 480 | 481 | newNexusNode = oldNexusNode 482 | 483 | compareResult, compareErr := tree.Compare(key, newNexusNode.Key) 484 | if compareErr != nil { 485 | err = compareErr 486 | return 487 | } 488 | 489 | switch { 490 | case compareResult < 0: // key < newNexusNode.Key 491 | updatedNewNexusNodeLeft, nonShadowingOk, insertErr := tree.insert(newNexusNode.left, key, value) 492 | if insertErr != nil { 493 | err = insertErr 494 | return 495 | } 496 | if !nonShadowingOk { 497 | ok = false 498 | err = nil 499 | return 500 | } 501 | newNexusNode.left = updatedNewNexusNodeLeft 502 | case compareResult > 0: // key > newNexusNode.Key 503 | updatedNewNexusNodeRight, nonShadowingOk, insertErr := tree.insert(newNexusNode.right, key, value) 504 | if insertErr != nil { 505 | err = insertErr 506 | return 507 | } 508 | if !nonShadowingOk { 509 | ok = false 510 | err = nil 511 | return 512 | } 513 | newNexusNode.right = updatedNewNexusNodeRight 514 | default: // compareResult == 0 (key == newNexusNode.Key) 515 | ok = false // Duplicate Key not supported 516 | err = nil 517 | 518 | return 519 | } 520 | 521 | ok = true // if we reach here, we will have succeeded 522 | err = nil 523 | 524 | newNexusNode = tree.fixUp(newNexusNode) 525 | 526 | return 527 | } 528 | 529 | func (tree *llrbTreeStruct) postInsertAdjustLen(node *llrbNodeStruct, key Key) (err error) { 530 | node.len++ 531 | 532 | compareResult, compareErr := tree.Compare(key, node.Key) 533 | if compareErr != nil { 534 | err = compareErr 535 | return 536 | } 537 | 538 | switch { 539 | case compareResult < 0: // key < node.Key 540 | tree.postInsertAdjustLen(node.left, key) 541 | case compareResult > 0: // key > node.Key 542 | tree.postInsertAdjustLen(node.right, key) 543 | default: // compareResult == 0 (key == node.Key) 544 | // nothing to do additionally here 545 | } 546 | 547 | err = nil 548 | 549 | return 550 | } 551 | 552 | func (tree *llrbTreeStruct) preDeleteByIndexAdjustLen(node *llrbNodeStruct, index int) (key Key) { 553 | node.len-- 554 | 555 | nodesOnLeft := 0 556 | if node.left != nil { 557 | nodesOnLeft = node.left.len 558 | } 559 | 560 | switch { 561 | case index < nodesOnLeft: // index indicates proceed down node.left 562 | key = tree.preDeleteByIndexAdjustLen(node.left, index) 563 | case index > nodesOnLeft: // index indicates proceed down node.right 564 | key = tree.preDeleteByIndexAdjustLen(node.right, (index - nodesOnLeft - 1)) 565 | default: // index == nodesOnLeft 566 | key = node.Key // return node.Key so that subsequent recursion step can use it 567 | } 568 | 569 | return 570 | } 571 | 572 | func (tree *llrbTreeStruct) preDeleteByKeyAdjustLen(node *llrbNodeStruct, key Key) (ok bool, err error) { 573 | if node == nil { 574 | ok = false 575 | err = nil 576 | 577 | return 578 | } 579 | 580 | compareResult, compareErr := tree.Compare(key, node.Key) 581 | if compareErr != nil { 582 | err = compareErr 583 | return 584 | } 585 | 586 | switch { 587 | case compareResult < 0: // key < node.Key 588 | ok, err = tree.preDeleteByKeyAdjustLen(node.left, key) 589 | if err != nil { 590 | return 591 | } 592 | if ok { 593 | node.len-- 594 | } 595 | case compareResult > 0: // key > node.Key 596 | ok, err = tree.preDeleteByKeyAdjustLen(node.right, key) 597 | if err != nil { 598 | return 599 | } 600 | if ok { 601 | node.len-- 602 | } 603 | default: // compareResult == 0 (key == node.Key) 604 | node.len-- 605 | ok = true 606 | } 607 | 608 | err = nil 609 | 610 | return 611 | } 612 | 613 | func (tree *llrbTreeStruct) delete(oldNexusNode *llrbNodeStruct, key Key) (newNexusNode *llrbNodeStruct, err error) { 614 | newNexusNode = oldNexusNode 615 | 616 | compareResult, compareErr := tree.Compare(key, newNexusNode.Key) 617 | if compareErr != nil { 618 | err = compareErr 619 | return 620 | } 621 | 622 | if compareResult < 0 { 623 | if isBlack(newNexusNode.left) && isBlack(newNexusNode.left.left) { 624 | newNexusNode = tree.moveRedLeft(newNexusNode) 625 | } 626 | 627 | newNexusNode.left, err = tree.delete(newNexusNode.left, key) 628 | if err != nil { 629 | return 630 | } 631 | } else { // compareResult >= 0 632 | if isRed(newNexusNode.left) { 633 | newNexusNode = tree.rotateRight(newNexusNode) 634 | 635 | compareResult, compareErr = tree.Compare(key, newNexusNode.Key) 636 | if compareErr != nil { 637 | err = compareErr 638 | return 639 | } 640 | } 641 | if (compareResult == 0) && (newNexusNode.right == nil) { 642 | newNexusNode = nil 643 | err = nil 644 | 645 | return 646 | } 647 | if isBlack(newNexusNode.right) && isBlack(newNexusNode.right.left) { 648 | newNexusNode = tree.moveRedRight(newNexusNode) 649 | 650 | compareResult, compareErr = tree.Compare(key, newNexusNode.Key) 651 | if compareErr != nil { 652 | err = compareErr 653 | return 654 | } 655 | } 656 | if compareResult == 0 { 657 | newNexusNode.right, newNexusNode.Key, newNexusNode.Value = tree.deleteMin(newNexusNode.right) 658 | } else { // compareResult != 0 659 | newNexusNode.right, err = tree.delete(newNexusNode.right, key) 660 | if err != nil { 661 | return 662 | } 663 | } 664 | } 665 | 666 | newNexusNode = tree.fixUp(newNexusNode) 667 | err = nil 668 | 669 | return 670 | } 671 | 672 | func (tree *llrbTreeStruct) deleteMin(oldNexusNode *llrbNodeStruct) (newNexusNode *llrbNodeStruct, minKey Key, minValue Value) { 673 | newNexusNode = oldNexusNode 674 | 675 | if newNexusNode.left == nil { 676 | minKey = newNexusNode.Key 677 | minValue = newNexusNode.Value 678 | newNexusNode = nil 679 | 680 | return 681 | } 682 | 683 | if isBlack(newNexusNode.left) && isBlack(newNexusNode.left.left) { 684 | newNexusNode = tree.moveRedLeft(newNexusNode) 685 | } 686 | 687 | newNexusNode.left, minKey, minValue = tree.deleteMin(newNexusNode.left) 688 | 689 | newNexusNode.len-- 690 | 691 | newNexusNode = tree.fixUp(newNexusNode) 692 | 693 | return 694 | } 695 | 696 | // Helper functions 697 | 698 | func isRed(node *llrbNodeStruct) bool { 699 | if node == nil { 700 | return false 701 | } 702 | 703 | // return (RED == node.color) 704 | return node.color 705 | } 706 | 707 | func isBlack(node *llrbNodeStruct) bool { 708 | if node == nil { 709 | return true 710 | } 711 | 712 | // return (BLACK == node.color) 713 | return !node.color 714 | } 715 | 716 | func colorFlip(node *llrbNodeStruct) { 717 | node.color = !node.color 718 | node.left.color = !node.left.color 719 | node.right.color = !node.right.color 720 | } 721 | 722 | func (*llrbTreeStruct) rotateLeft(oldParentNode *llrbNodeStruct) (newParentNode *llrbNodeStruct) { 723 | // Adjust children fields 724 | 725 | newParentNode = oldParentNode.right 726 | oldParentNode.right = newParentNode.left 727 | newParentNode.left = oldParentNode 728 | 729 | // Adjust len fields 730 | 731 | nodesTransferred := 0 732 | if oldParentNode.right != nil { 733 | nodesTransferred = oldParentNode.right.len 734 | } 735 | 736 | oldParentNode.len = oldParentNode.len - newParentNode.len + nodesTransferred 737 | newParentNode.len = newParentNode.len - nodesTransferred + oldParentNode.len 738 | 739 | // Adjust color field 740 | 741 | newParentNode.color = oldParentNode.color 742 | oldParentNode.color = RED 743 | 744 | // All done 745 | 746 | return 747 | } 748 | 749 | func (*llrbTreeStruct) rotateRight(oldParentNode *llrbNodeStruct) (newParentNode *llrbNodeStruct) { 750 | // Adjust children fields 751 | 752 | newParentNode = oldParentNode.left 753 | oldParentNode.left = newParentNode.right 754 | newParentNode.right = oldParentNode 755 | 756 | // Adjust len fields 757 | 758 | nodesTransferred := 0 759 | if oldParentNode.left != nil { 760 | nodesTransferred = oldParentNode.left.len 761 | } 762 | 763 | oldParentNode.len = oldParentNode.len - newParentNode.len + nodesTransferred 764 | newParentNode.len = newParentNode.len - nodesTransferred + oldParentNode.len 765 | 766 | // Adjust color field 767 | 768 | newParentNode.color = oldParentNode.color 769 | oldParentNode.color = RED 770 | 771 | // All done 772 | 773 | return 774 | } 775 | 776 | func (tree *llrbTreeStruct) moveRedLeft(oldNexusNode *llrbNodeStruct) (newNexusNode *llrbNodeStruct) { 777 | newNexusNode = oldNexusNode 778 | 779 | colorFlip(newNexusNode) 780 | 781 | if isRed(newNexusNode.right.left) { 782 | newNexusNode.right = tree.rotateRight(newNexusNode.right) 783 | newNexusNode = tree.rotateLeft(newNexusNode) 784 | colorFlip(newNexusNode) 785 | } 786 | 787 | return 788 | } 789 | 790 | func (tree *llrbTreeStruct) moveRedRight(oldNexusNode *llrbNodeStruct) (newNexusNode *llrbNodeStruct) { 791 | newNexusNode = oldNexusNode 792 | 793 | colorFlip(newNexusNode) 794 | 795 | if isRed(newNexusNode.left.left) { 796 | newNexusNode = tree.rotateRight(newNexusNode) 797 | colorFlip(newNexusNode) 798 | } 799 | 800 | return 801 | } 802 | 803 | func (tree *llrbTreeStruct) fixUp(oldNexusNode *llrbNodeStruct) (newNexusNode *llrbNodeStruct) { 804 | newNexusNode = oldNexusNode 805 | 806 | if isRed(newNexusNode.right) { 807 | newNexusNode = tree.rotateLeft(newNexusNode) 808 | } 809 | if isRed(newNexusNode.left) && isRed(newNexusNode.left.left) { 810 | newNexusNode = tree.rotateRight(newNexusNode) 811 | } 812 | if isRed(newNexusNode.left) && isRed(newNexusNode.right) { 813 | colorFlip(newNexusNode) 814 | } 815 | 816 | return 817 | } 818 | -------------------------------------------------------------------------------- /llrb_tree_api.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | type LLRBTree interface { 7 | SortedMap 8 | Reset() 9 | } 10 | 11 | // LLRBTreeCallbacks specifies the interface to a set of callbacks provided by the client 12 | type LLRBTreeCallbacks interface { 13 | DumpCallbacks 14 | } 15 | 16 | func NewLLRBTree(compare Compare, callbacks LLRBTreeCallbacks) (tree LLRBTree) { 17 | tree = &llrbTreeStruct{Compare: compare, LLRBTreeCallbacks: callbacks, root: nil} 18 | return 19 | } 20 | -------------------------------------------------------------------------------- /llrb_validate.go: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015-2025, NVIDIA CORPORATION. 2 | // SPDX-License-Identifier: Apache-2.0 3 | 4 | package sortedmap 5 | 6 | import "fmt" 7 | 8 | func (tree *llrbTreeStruct) Validate() (err error) { 9 | tree.Lock() 10 | defer tree.Unlock() 11 | 12 | err = tree.root.validate() 13 | 14 | return 15 | } 16 | 17 | func (node *llrbNodeStruct) validate() (err error) { 18 | if node == nil { 19 | return nil 20 | } 21 | 22 | computedHeight := 1 23 | 24 | if node.left != nil { 25 | computedHeight += node.left.len 26 | 27 | err = node.left.validate() 28 | 29 | if err != nil { 30 | return 31 | } 32 | } 33 | 34 | if node.right != nil { 35 | computedHeight += node.right.len 36 | 37 | err = node.right.validate() 38 | 39 | if err != nil { 40 | return 41 | } 42 | } 43 | 44 | if computedHeight != node.len { 45 | err = fmt.Errorf("For node.Key == %v, computedHeight(%v) != node.len(%v)", node.Key, computedHeight, node.len) 46 | return 47 | } 48 | 49 | return nil 50 | } 51 | --------------------------------------------------------------------------------