├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── amt └── amt.h └── test └── lower_bound.cpp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build* 2 | .vagrant 3 | **/.vscode 4 | TAGS 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AMT - Fast integer trie map 2 | 3 | This is based on Phil Bagwell's `Array Mapped Tree` from ["Fast And Space Efficient Trie Searches"](https://idea.popcount.org/2012-07-25-introduction-to-hamt/triesearches.pdf) 4 | 5 | It supports unsigned integer keys (32, 64 or 128 bits) and has been specially optimised for the case where keys are somewhat monotonic. 6 | 7 | ## Overview 8 | 9 | - **Header only**: the file `amt.h` is all you need 10 | 11 | - **ordered container** 12 | 13 | - **very fast** - performance similar to hash table 14 | 15 | - **mostly same interface as `std::map`**. However the value_type has both the `key` and the `value` const. 16 | 17 | - **low memory usage** 18 | 19 | 20 | -------------------------------------------------------------------------------- /amt/amt.h: -------------------------------------------------------------------------------- 1 | #if !defined(amt_h_guard_) 2 | #define amt_h_guard_ 3 | 4 | // --------------------------------------------------------------------------- 5 | // Copyright (c) 2020, Gregory Popovitch - greg7mdp@gmail.com 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // https://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | // --------------------------------------------------------------------------- 19 | 20 | #ifdef _MSC_VER 21 | #pragma warning(push) 22 | 23 | #pragma warning(disable : 4127) // conditional expression is constant 24 | #pragma warning(disable : 4324) // structure was padded due to alignment specifier 25 | #pragma warning(disable : 4514) // unreferenced inline function has been removed 26 | #pragma warning(disable : 4623) // default constructor was implicitly defined as deleted 27 | #pragma warning(disable : 4625) // copy constructor was implicitly defined as deleted 28 | #pragma warning(disable : 4626) // assignment operator was implicitly defined as deleted 29 | #pragma warning(disable : 4710) // function not inlined 30 | #pragma warning(disable : 4711) // selected for automatic inline expansion 31 | #pragma warning(disable : 4820) // '6' bytes padding added after data member 32 | #pragma warning(disable : 4868) // compiler may not enforce left-to-right evaluation order in braced initializer list 33 | #pragma warning(disable : 5027) // move assignment operator was implicitly defined as deleted 34 | #pragma warning(disable : 5045) // Compiler will insert Spectre mitigation for memory load if /Qspectre switch specified 35 | #endif 36 | 37 | 38 | 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | #include 45 | #include 46 | #include 47 | #include 48 | #include 49 | 50 | #ifdef _MSC_VER 51 | #include // for __popcnt() 52 | #endif 53 | 54 | namespace amt_ // internal namespace 55 | { 56 | 57 | // --------------------------------------------------------------------------- 58 | // popcount stuff 59 | // --------------------------------------------------------------------------- 60 | static inline uint32_t s_amt_popcount_default(uint32_t i) noexcept 61 | { 62 | i = i - ((i >> 1) & 0x55555555); 63 | i = (i & 0x33333333) + ((i >> 2) & 0x33333333); 64 | return (((i + (i >> 4)) & 0x0F0F0F0F) * 0x01010101) >> 24; 65 | } 66 | 67 | static inline uint32_t s_amt_popcount_default(uint64_t x) noexcept 68 | { 69 | const uint64_t m1 = uint64_t(0x5555555555555555); // binary: 0101... 70 | const uint64_t m2 = uint64_t(0x3333333333333333); // binary: 00110011.. 71 | const uint64_t m4 = uint64_t(0x0f0f0f0f0f0f0f0f); // binary: 4 zeros, 4 ones ... 72 | const uint64_t h01 = uint64_t(0x0101010101010101); // the sum of 256 to the power of 0,1,2,3... 73 | 74 | x -= (x >> 1) & m1; // put count of each 2 bits into those 2 bits 75 | x = (x & m2) + ((x >> 2) & m2); // put count of each 4 bits into those 4 bits 76 | x = (x + (x >> 4)) & m4; // put count of each 8 bits into those 8 bits 77 | return (x * h01)>>56; // returns left 8 bits of x + (x<<8) + (x<<16) + (x<<24)+... 78 | } 79 | 80 | #if defined __clang__ 81 | 82 | #if defined(i386) 83 | #include 84 | inline void amt_cpuid(int info[4], int InfoType) { 85 | __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]); 86 | } 87 | #endif 88 | 89 | #define AMT_POPCNT __builtin_popcount 90 | #define AMT_POPCNT64 __builtin_popcountll 91 | 92 | #elif defined __GNUC__ 93 | 94 | #if defined(i386) 95 | #include 96 | inline void amt_cpuid(int info[4], int InfoType) { 97 | __cpuid_count(InfoType, 0, info[0], info[1], info[2], info[3]); 98 | } 99 | #endif 100 | 101 | // __POPCNT__ defined when the compiled with popcount support 102 | // (-mpopcnt compiler option is given for example) 103 | #ifdef __POPCNT__ 104 | // slower unless compiled iwith -mpopcnt 105 | #define AMT_POPCNT __builtin_popcount 106 | #define AMT_POPCNT64 __builtin_popcountll 107 | #endif 108 | 109 | 110 | #elif defined _MSC_VER 111 | 112 | // I don't think we have to check! 113 | // #define AMT_POPCNT_CHECK // slower when defined, but we have to check! 114 | #define amt_cpuid(info, x) __cpuid(info, x) 115 | 116 | #define AMT_POPCNT __popcnt 117 | #if (INTPTR_MAX == INT64_MAX) 118 | #define AMT_POPCNT64 __popcnt64 119 | #endif 120 | 121 | #endif 122 | 123 | #if defined(AMT_POPCNT_CHECK) 124 | static inline bool amt_popcount_check() 125 | { 126 | int cpuInfo[4] = { -1 }; 127 | amt_cpuid(cpuInfo, 1); 128 | if (cpuInfo[2] & (1 << 23)) 129 | return true; // means AMT_POPCNT supported 130 | return false; 131 | } 132 | #endif 133 | 134 | #if defined(AMT_POPCNT_CHECK) && defined(AMT_POPCNT) 135 | 136 | static inline uint32_t amt_popcount(uint32_t i) 137 | { 138 | static const bool s_ok = amt_popcount_check(); 139 | return s_ok ? AMT_POPCNT(i) : s_amt_popcount_default(i); 140 | } 141 | 142 | #else 143 | 144 | static inline uint32_t amt_popcount(uint32_t i) 145 | { 146 | #if defined(AMT_POPCNT) 147 | return static_cast(AMT_POPCNT(i)); 148 | #else 149 | return s_amt_popcount_default(i); 150 | #endif 151 | } 152 | 153 | #endif 154 | 155 | // -------------------------------------------------------------------------- 156 | // for leaves, SV and V are the same 157 | // for non-leaves, SV is a sparsegroup *, and V is the actual amt mapped_type 158 | // -------------------------------------------------------------------------- 159 | template 160 | class sparsegroup 161 | { 162 | using bm_type = uint16_t; 163 | using size_type = uint8_t; 164 | using group = sparsegroup; 165 | using group_ptr = group *; 166 | using leaf_group = sparsegroup; 167 | using leaf_group_ptr = leaf_group *; 168 | 169 | public: 170 | struct locator 171 | { 172 | locator(sparsegroup *g, K key, uint32_t idx) : 173 | _group(g), _key(key), _idx(idx) 174 | {} 175 | 176 | sparsegroup *_group; 177 | K _key; 178 | uint32_t _idx; 179 | }; 180 | 181 | struct insert_locator : public locator 182 | { 183 | insert_locator(sparsegroup *g, K key, uint32_t idx, bool created) : 184 | locator(g, key, idx), _created(created) 185 | {} 186 | 187 | bool _created; 188 | }; 189 | 190 | sparsegroup(sparsegroup *parent, uint32_t depth, uint32_t num_alloc, K key) : 191 | _bitmap(0), 192 | _num_val(0), 193 | _num_alloc(num_alloc), 194 | _depth(depth), 195 | _partial_key(_make_partial(key)), 196 | _parent(parent) 197 | { 198 | } 199 | 200 | sparsegroup(const sparsegroup &) = delete; 201 | sparsegroup(sparsegroup &&) = delete; 202 | sparsegroup& operator=(const sparsegroup &) = delete; 203 | sparsegroup& operator=(sparsegroup &&) = delete; 204 | ~sparsegroup() = delete; 205 | 206 | K partial_key() const { return _partial_key; } 207 | bool is_leaf() const { return _depth == max_depth; } 208 | uint32_t depth() { return _depth; } 209 | 210 | size_type parent_idx() const 211 | { 212 | size_type parent_nibble = _parent->_nibble(_partial_key); 213 | return _parent->_nibble_to_idx(parent_nibble); 214 | } 215 | 216 | void shrink() 217 | { 218 | assert(is_leaf() && _parent); 219 | if (_num_alloc - _num_val <= 1) 220 | return; 221 | uint32_t new_size = (_num_val + 1) & ~0x1; 222 | if (_num_alloc > new_size) 223 | resize(new_size); 224 | } 225 | 226 | void swap(sparsegroup &o) 227 | { 228 | using std::swap; 229 | for (uint32_t i=0; i<_num_val; ++i) 230 | swap(_values[i], o._values[i]); 231 | 232 | // swap _num_val 233 | uint32_t tmp = _num_val; 234 | _num_val = o._num_val; 235 | o._num_val = tmp; 236 | } 237 | 238 | group_ptr resize(uint32_t sz) 239 | { 240 | if (_depth == 0) 241 | return this; // don't resize root sparsegroup as we can't update the parent 242 | assert(sz >= _num_val); 243 | group_ptr grp = allocate_group(sz, _parent, _depth, _partial_key); 244 | grp->_bitmap = _bitmap; 245 | 246 | if (is_leaf()) 247 | reinterpret_cast(this)->swap(*reinterpret_cast(grp)); 248 | else 249 | { 250 | for (uint32_t i=0; i<_num_val; ++i) 251 | reinterpret_cast(_values[i])->_parent = grp; 252 | this->swap(*grp); 253 | } 254 | 255 | size_type parent_nibble = _parent->_nibble(_partial_key); 256 | size_type parent_idx = _parent->_nibble_to_idx(parent_nibble); 257 | assert((group_ptr)_parent->get_value(parent_idx) == this); 258 | _parent->set_value(parent_idx, (SV)grp); 259 | 260 | deallocate_group(); 261 | return grp; 262 | } 263 | 264 | group_ptr root() 265 | { 266 | if (!_parent) 267 | return this; 268 | else 269 | return _parent->root(); 270 | } 271 | 272 | static locator end() 273 | { 274 | return { nullptr, 0, 0 }; 275 | } 276 | 277 | locator first() const 278 | { 279 | if (is_leaf()) 280 | { 281 | assert(_num_val > 0); 282 | return { const_cast(this), get_key(0), 0 }; 283 | } 284 | else if (_num_val > 0) 285 | { 286 | return reinterpret_cast(_values[0])->first(); 287 | } 288 | return end(); 289 | } 290 | 291 | locator last() const 292 | { 293 | if (is_leaf()) 294 | { 295 | assert(_num_val > 0); 296 | return { const_cast(this), get_key(_num_val - 1), (uint32_t)(_num_val - 1) }; 297 | } 298 | else if (_num_val > 0) 299 | { 300 | return reinterpret_cast(_values[_num_val - 1])->last(); 301 | } 302 | return end(); 303 | } 304 | 305 | locator next(uint32_t idx) const 306 | { 307 | assert(_num_val > idx); 308 | ++idx; 309 | if (idx < _num_val) 310 | { 311 | if (is_leaf()) 312 | return { const_cast(this), get_key(idx), idx }; 313 | else 314 | return reinterpret_cast(_values[idx])->first(); 315 | } 316 | else 317 | { 318 | if (_depth > 0) 319 | return _parent->next(parent_idx()); 320 | else 321 | return end(); 322 | } 323 | } 324 | 325 | locator previous(uint32_t idx) const 326 | { 327 | // if on first element, no change 328 | if (idx > 0) 329 | { 330 | --idx; 331 | if (is_leaf()) 332 | return { const_cast(this), get_key(idx), idx }; 333 | else 334 | return reinterpret_cast(_values[idx])->last(); 335 | } 336 | else 337 | { 338 | if (_depth > 0) 339 | return _parent->previous(parent_idx()); 340 | else 341 | return first(); 342 | } 343 | } 344 | 345 | locator find(K key) const 346 | { 347 | size_type n = _nibble(key); 348 | 349 | if (_bmtest(n)) 350 | { 351 | // found it 352 | uint32_t idx = _nibble_to_idx(n); 353 | if (is_leaf()) 354 | return { const_cast(this), key, idx }; 355 | else 356 | return reinterpret_cast(_values[idx])->find(key); 357 | } 358 | else 359 | return { nullptr, 0, 0 }; 360 | } 361 | 362 | 363 | locator find_ge(K key) const 364 | { 365 | size_type nkey = _nibble(key); 366 | if (_bmtest(nkey)) 367 | { 368 | uint32_t idx = _nibble_to_idx(nkey); 369 | if (is_leaf()) 370 | return { const_cast(this), get_key(idx), idx }; 371 | else 372 | { 373 | auto loc = reinterpret_cast(_values[idx])->find_ge(key); 374 | if (loc._group) 375 | return loc; 376 | } 377 | } 378 | 379 | size_type n = _nibble_to_ge_nibble(nkey); 380 | if (n < nibble_max) 381 | { 382 | assert(_bmtest(n)); 383 | uint32_t idx = _nibble_to_idx(n); 384 | if (is_leaf()) 385 | return { const_cast(this), get_key(idx), idx }; 386 | return reinterpret_cast(_values[idx])->first(); 387 | } else 388 | return next(_num_val - 1); 389 | 390 | return { nullptr, 0, 0 }; 391 | } 392 | 393 | bool _erase(uint32_t idx) 394 | { 395 | assert(_num_val > 0 && idx < _num_val); 396 | if (_num_val == 1 && _parent) 397 | { 398 | size_type pidx = parent_idx(); 399 | assert((group_ptr)_parent->get_value(pidx) == this); 400 | _parent->_erase(pidx); 401 | return true; 402 | } 403 | else if (_num_val) 404 | { 405 | _bmclear(_idx_to_nibble(idx)); 406 | if (is_leaf()) 407 | { 408 | V *v = reinterpret_cast(&_values[0]); 409 | std::rotate(v + idx, v + idx + 1, v + _num_val); 410 | (v + _num_val - 1)->~SV(); 411 | } 412 | else 413 | { 414 | group_ptr *v = reinterpret_cast(&_values[0]); 415 | std::rotate(v + idx, v + idx + 1, v + _num_val); 416 | (v[_num_val - 1])->deallocate_group(); 417 | } 418 | --_num_val; 419 | } 420 | return false; 421 | } 422 | 423 | // returns true if a leaf group was freed 424 | bool erase(uint32_t idx) 425 | { 426 | assert(is_leaf()); 427 | return reinterpret_cast(this)->_erase(idx); 428 | } 429 | 430 | insert_locator find_or_prepare_insert(K key) 431 | { 432 | size_type n = _nibble(key); 433 | 434 | if (_bmtest(n)) // found it 435 | { 436 | size_type idx = _nibble_to_idx(n); 437 | if (_depth == max_depth) 438 | return { this, key, idx, false }; 439 | else 440 | return reinterpret_cast(_values[idx])->find_or_prepare_insert(key); 441 | } 442 | else 443 | { 444 | // need to insert 445 | if (_num_val >= _num_alloc) 446 | { 447 | // need to resize 448 | assert(_depth > 0); 449 | uint32_t new_size = _num_alloc + 1; 450 | new_size = (new_size + 3) & ~0x3; // multiples of 4... use extra memory for speed 451 | assert(new_size <= 16); 452 | group_ptr grp = resize(new_size); 453 | return grp->find_or_prepare_insert(key);; 454 | } 455 | size_type idx = _nibble_to_idx(n); 456 | _bmset(n); 457 | if (is_leaf()) 458 | { 459 | reinterpret_cast(this)->prepare_for_insert(idx); 460 | ++_num_val; 461 | return { this, key, idx, true }; 462 | } 463 | else 464 | { 465 | prepare_for_insert(idx); 466 | ++_num_val; 467 | _values[idx] = (SV)allocate_group(_depth + 1 == max_depth ? 16 : 2, this, _depth + 1, key); 468 | return reinterpret_cast(_values[idx])->find_or_prepare_insert(key); 469 | } 470 | } 471 | return { nullptr, 0, 0, false }; 472 | } 473 | 474 | bool match(K key) const 475 | { 476 | assert(_depth == max_depth); // we are matching only leaf groups 477 | return (key & ~bm_mask) == _partial_key; 478 | } 479 | 480 | K get_key(size_type idx) const 481 | { 482 | return _partial_key | _idx_to_nibble(idx); 483 | } 484 | 485 | template 486 | void set_value(uint32_t idx, Val &&v) 487 | { 488 | _values[idx] = std::forward(v); 489 | } 490 | 491 | SV& get_value(uint32_t idx) 492 | { 493 | return _values[idx]; 494 | } 495 | 496 | void set_new_group(uint32_t idx, group_ptr grp) 497 | { 498 | _values[idx] = grp; 499 | } 500 | 501 | static group_ptr allocate_group(uint32_t num_alloc, group_ptr parent, uint32_t depth, K partial_key) 502 | { 503 | bool leaf = (depth == max_depth); 504 | if (!leaf) 505 | { 506 | group_ptr grp = (group_ptr)malloc(group_size_in_bytes(num_alloc)); 507 | new (grp) group(parent, depth, num_alloc, partial_key); 508 | for (uint32_t i=0; i_values[i] = nullptr; 510 | return grp; 511 | } 512 | else 513 | { 514 | leaf_group_ptr grp = (leaf_group_ptr)malloc(group_size_in_bytes(num_alloc)); 515 | new (grp) leaf_group((leaf_group_ptr)parent, depth, num_alloc, partial_key); 516 | // grp->construct_values(); 517 | return (group_ptr)grp; 518 | } 519 | } 520 | 521 | static group_ptr allocate_group(const sparsegroup &o, group_ptr parent) 522 | { 523 | // copies the group and all children 524 | bool leaf = (o._depth == max_depth); 525 | uint32_t num_val = o._num_val; 526 | group_ptr grp = allocate_group(o._num_alloc, parent, o._depth, o._partial_key); 527 | grp->_bitmap = o._bitmap; 528 | grp->_num_val = num_val; 529 | 530 | if (!leaf) 531 | { 532 | for (uint32_t i=0; i(grp->_values[i]) = 534 | allocate_group(reinterpret_cast(*o._values[i]), grp); 535 | } 536 | else 537 | { 538 | for (uint32_t i=0; iconstruct_value(i); 541 | grp->set_value(i, o._values[i]); 542 | } 543 | } 544 | return grp; 545 | } 546 | 547 | void construct_value(uint32_t idx) 548 | { 549 | new (&_values[idx]) SV(); 550 | } 551 | 552 | void construct_values() 553 | { 554 | for (uint32_t i=0; i<_num_val; ++i) 555 | construct_value(i); 556 | } 557 | 558 | void prepare_for_insert(uint32_t idx) 559 | { 560 | assert(_num_val < _num_alloc); 561 | construct_value(_num_val); 562 | SV *v = &_values[0]; 563 | std::rotate(v + idx, v + _num_val, v + _num_val + 1); 564 | } 565 | 566 | 567 | // deallocates the group and all its children 568 | void deallocate_group() 569 | { 570 | bool leaf = (_depth == max_depth); 571 | if (leaf) 572 | reinterpret_cast(this)->deallocate_leaf(); 573 | else 574 | { 575 | for (uint32_t i=0; i<_num_val; ++i) 576 | reinterpret_cast(_values[i])->deallocate_group(); 577 | free(this); 578 | } 579 | } 580 | 581 | void deallocate_leaf() 582 | { 583 | for (uint32_t i=0; i<_num_val; ++i) 584 | _values[i].~V(); 585 | free(this); 586 | } 587 | 588 | private: 589 | 590 | bool _bmtest(size_type i) const { return !!(_bitmap & (static_cast(1) << i)); } 591 | void _bmset(size_type i) { _bitmap |= static_cast(1) << i; } 592 | void _bmclear(size_type i) { _bitmap &= ~(static_cast(1) << i); } 593 | 594 | // count trailing zeroes 595 | static uint32_t _ctz32(uint32_t v) 596 | { 597 | unsigned int c = 32; // c will be the number of zero bits on the right 598 | v &= -signed(v); 599 | if (v) c--; 600 | if (v & 0x0000FFFF) c -= 16; 601 | if (v & 0x00FF00FF) c -= 8; 602 | if (v & 0x0F0F0F0F) c -= 4; 603 | if (v & 0x33333333) c -= 2; 604 | if (v & 0x55555555) c -= 1; 605 | return c; 606 | } 607 | 608 | uint32_t _nibble_to_ge_nibble(size_type nibble) const 609 | { 610 | uint16_t bm = _bitmap & ~((static_cast(1) << nibble) - 1); 611 | return _ctz32(bm); 612 | } 613 | 614 | template 615 | static size_t group_size_in_bytes(uint32_t cnt) 616 | { 617 | assert(cnt >= 1); 618 | size_t x = sizeof(group) + (cnt - 1) * sizeof(Val); 619 | return (x + 0xF) & ~0xF; 620 | } 621 | 622 | // position in bitmap, to index in array of values 623 | size_type _nibble_to_idx(size_type nibble) const 624 | { 625 | return static_cast(amt_popcount(_bitmap & ((static_cast(1) << nibble) - 1))); 626 | } 627 | 628 | #ifdef _MSC_VER 629 | #pragma warning(push) 630 | #pragma warning(disable : 4146) 631 | #endif 632 | 633 | // index in array of values to position in bitmap 634 | size_type _idx_to_nibble(size_type idx) const 635 | { 636 | uint32_t bm = _bitmap; 637 | 638 | for (; idx > 0; idx--) 639 | bm &= (bm-1); // remove right-most set bit 640 | 641 | // Clear all bits to the left of the rightmost bit (the &), 642 | // and then clear the rightmost bit but set all bits to the 643 | // right of it (the -1). 644 | // -------------------------------------------------------- 645 | bm = (bm & -bm) - 1; 646 | return static_cast(amt_popcount(bm)); 647 | } 648 | 649 | 650 | #ifdef _MSC_VER 651 | #pragma warning(pop) 652 | #endif 653 | 654 | size_type _nibble(K key) const 655 | { 656 | return (size_type)((key >> ((max_depth - _depth) * bm_shift)) & bm_mask); 657 | } 658 | 659 | K _make_partial(K key) const 660 | { 661 | return key & ~((1 << ((max_depth - _depth + 1) * bm_shift)) - 1); 662 | } 663 | 664 | static constexpr const uint32_t bm_shift = 4; 665 | static constexpr const uint32_t bm_mask = 0xF; 666 | static constexpr const uint32_t max_depth = (sizeof(K) * 8) / bm_shift - 1; 667 | static constexpr const uint32_t nibble_max = 16; 668 | 669 | #if 0 670 | uint32_t _bitmap : 16; 671 | uint32_t _num_val : 5; 672 | uint32_t _num_alloc : 5; 673 | uint32_t _depth : 6; 674 | #else 675 | uint16_t _bitmap; 676 | uint8_t _num_val; 677 | uint8_t _num_alloc; 678 | uint8_t _depth; 679 | #endif 680 | K _partial_key; // only highest bits 0 -> 4 * depth are set 681 | group_ptr _parent; 682 | SV _values[N]; 683 | }; 684 | 685 | 686 | }; // amt_ 687 | 688 | namespace amt 689 | { 690 | using namespace amt_; 691 | 692 | // ----------------------------------------------------------------------------- 693 | // An Array Mapped Tree (amt) is an ordered associative container which mapping 694 | // unsigned integer keys to values. It is optimized for both speed and memory 695 | // footprint in most common use cases. 696 | // see paper: "Fast And Space Efficient Trie Searches" by Phil Bagwell 697 | // http://lampwww.epfl.ch/papers/triesearches.pdf.gz 698 | // 699 | // Its interface is similar to that of `std::unordered_map`. 700 | // ----------------------------------------------------------------------------- 701 | template 702 | class amt 703 | { 704 | using leaf_group = sparsegroup; 705 | using leaf_group_ptr = leaf_group *; 706 | 707 | using group = sparsegroup; 708 | using group_ptr = group *; 709 | 710 | using locator = typename group::locator; 711 | using insert_locator = typename group::insert_locator; 712 | 713 | public: 714 | 715 | static_assert(std::is_integral::value && std::is_unsigned::value && 716 | (sizeof(K) == 2 || sizeof(K) == 4 || sizeof(K) == 8), "supporting 16, 32 or 64 bit unsigned integer keys."); 717 | 718 | static bool _equal(const K &a, const K &b) { return a == b; } 719 | 720 | using key_type = K; 721 | using value_type = std::pair; 722 | using mapped_type = V; 723 | using size_type = std::size_t; 724 | using difference_type = std::ptrdiff_t; 725 | using key_equal = std::equal_to; 726 | 727 | 728 | // --------------------- iterator --------------------------------------- 729 | class iterator 730 | { 731 | friend class amt; 732 | using group = typename amt::group; 733 | using group_ptr = typename amt::group_ptr; 734 | using leaf_group_ptr = typename amt::leaf_group_ptr; 735 | using locator = typename amt::locator; 736 | 737 | public: 738 | using iterator_category = std::forward_iterator_tag; 739 | using value_type = typename amt::value_type; 740 | using key_type = typename amt::key_type; 741 | using mapped_type = typename amt::mapped_type; 742 | using reference = value_type&; 743 | using const_reference = const value_type&; 744 | using pointer = value_type *; 745 | using const_pointer = const value_type *; 746 | using difference_type = typename amt::difference_type; 747 | 748 | iterator(const locator &loc) : 749 | _group(loc._group), _idx(loc._idx) 750 | { 751 | assert(_group); 752 | _set_vt(loc._key); 753 | } 754 | 755 | iterator(group_ptr root) : 756 | _group(root), _idx(0) 757 | { 758 | assert(_group && _group->depth() == 0); // creating end iterator with root 759 | } 760 | 761 | iterator(const iterator &o) : 762 | _group(o._group), _idx(o._idx) 763 | { 764 | assert(_group); 765 | if (!_at_end()) 766 | { 767 | *const_cast(&_v.first) = o._v.first; 768 | *const_cast(&_v.second) = o._v.second; 769 | } 770 | } 771 | 772 | iterator& operator=(const iterator &o) 773 | { 774 | assert(o._group); 775 | _group = o._group; 776 | _idx = o._idx; 777 | if (!_at_end()) 778 | { 779 | *const_cast(&_v.first) = o._v.first; 780 | *const_cast(&_v.second) = o._v.second; 781 | } 782 | return *this; 783 | } 784 | 785 | const_reference operator*() const { assert(!_at_end()); return _v; } 786 | reference operator*() { assert(!_at_end()); return _v; } 787 | 788 | const_pointer operator->() const { return &operator*(); } 789 | pointer operator->() { return &operator*(); } 790 | 791 | const key_type& key() const { return _v.first; } 792 | 793 | iterator& operator--() 794 | { 795 | if (_at_end()) 796 | { 797 | auto loc = _group->last(); 798 | *this = loc._group ? iterator(loc) : iterator(_group->root()); 799 | } 800 | else 801 | *this = iterator(_group->previous(_idx)); 802 | return *this; 803 | } 804 | 805 | iterator operator--(int) 806 | { 807 | auto tmp = *this; 808 | --*this; 809 | return tmp; 810 | } 811 | 812 | iterator& operator++() 813 | { 814 | assert(!_at_end()); 815 | auto loc = _group->next(_idx); 816 | *this = loc._group ? iterator(loc) : iterator(_group->root()); 817 | return *this; 818 | } 819 | 820 | iterator operator++(int) 821 | { 822 | auto tmp = *this; 823 | ++*this; 824 | return tmp; 825 | } 826 | 827 | friend bool operator==(const iterator& a, const iterator& b) 828 | { 829 | return a._group == b._group && a._idx == b._idx; 830 | } 831 | 832 | friend bool operator!=(const iterator& a, const iterator& b) 833 | { 834 | return !(a == b); 835 | } 836 | 837 | private: 838 | bool _at_end() const 839 | { 840 | return !_group->depth(); 841 | } 842 | 843 | void _set_vt(K key) 844 | { 845 | if (!_at_end()) 846 | { 847 | assert(_group->is_leaf()); 848 | *const_cast(&_v.first) = key; 849 | *const_cast(&_v.second) = reinterpret_cast(_group)->get_value(_idx); 850 | } 851 | } 852 | 853 | group_ptr _group; 854 | uint32_t _idx; 855 | 856 | // anonymous union to avoid uninitialized member warnings 857 | union { 858 | value_type _v; 859 | }; 860 | }; 861 | 862 | // --------------- const iterator --------------------------------------- 863 | class const_iterator 864 | { 865 | friend class amt; 866 | 867 | public: 868 | using iterator_category = std::forward_iterator_tag; 869 | using value_type = typename amt::value_type; 870 | using reference = const value_type&; 871 | using pointer = const value_type *; 872 | using difference_type = typename amt::difference_type; 873 | 874 | const_iterator() {} 875 | const_iterator(iterator it) : _it(std::move(it)) {} 876 | 877 | reference operator*() const { return *_it; } 878 | pointer operator->() const { return _it.operator->(); } 879 | 880 | const key_type& key() const { return _it.key(); } 881 | 882 | const_iterator& operator++() 883 | { 884 | ++_it; 885 | return *this; 886 | } 887 | const_iterator& operator--() 888 | { 889 | --_it; 890 | return *this; 891 | } 892 | const_iterator operator++(int) { return _it++; } 893 | const_iterator operator--(int) { return _it--; } 894 | 895 | friend bool operator==(const const_iterator& a, const const_iterator& b) 896 | { 897 | return a._it == b._it; 898 | } 899 | 900 | friend bool operator!=(const const_iterator& a, const const_iterator& b) 901 | { 902 | return !(a == b); 903 | } 904 | 905 | private: 906 | iterator _it; 907 | }; 908 | 909 | using const_reverse_iterator = std::reverse_iterator; 910 | using reverse_iterator = std::reverse_iterator; 911 | 912 | // --------------------- constructors ----------------------------------- 913 | amt() noexcept : 914 | _last(nullptr), 915 | _root(nullptr), 916 | _size(0) 917 | { 918 | _init(); 919 | } 920 | 921 | template 922 | amt(InputIter first, InputIter last) : amt() 923 | { 924 | insert(first, last); 925 | } 926 | 927 | amt(std::initializer_list init) : amt(init.begin(), init.end()) 928 | {} 929 | 930 | amt(const amt &o) : amt() 931 | { 932 | _size = o._size; 933 | if (o._root) 934 | _root = group::allocate_group(*o._root, nullptr); 935 | else 936 | _init(); 937 | } 938 | 939 | amt(amt &&o) noexcept : amt() 940 | { 941 | swap(o); 942 | } 943 | 944 | amt& operator=(const amt &o) 945 | { 946 | amt tmp(o); 947 | swap(tmp); 948 | return *this; 949 | } 950 | 951 | amt& operator=(amt &&o) 952 | { 953 | amt tmp(std::move(o)); 954 | swap(tmp); 955 | return *this; 956 | } 957 | 958 | // --------------------- desstructor ----------------------------------- 959 | ~amt() 960 | { 961 | _cleanup(); 962 | } 963 | 964 | // --------------------- iterators ------------------------------------------ 965 | iterator begin() 966 | { 967 | auto loc = _root->first(); 968 | return _iter(loc); 969 | } 970 | 971 | iterator end() 972 | { 973 | return iterator(_root); 974 | } 975 | 976 | const_iterator begin() const { return const_cast(this)->begin(); } 977 | const_iterator end() const { return const_cast(this)->end(); } 978 | const_iterator cbegin() const { return begin(); } 979 | const_iterator cend() const { return end(); } 980 | 981 | reverse_iterator rbegin() 982 | { 983 | return reverse_iterator(end()); 984 | } 985 | 986 | const_reverse_iterator rbegin() const 987 | { 988 | return const_reverse_iterator(end()); 989 | } 990 | 991 | reverse_iterator rend() 992 | { 993 | return reverse_iterator(begin()); 994 | } 995 | 996 | const_reverse_iterator rend() const 997 | { 998 | return const_reverse_iterator(begin()); 999 | } 1000 | 1001 | // Finds the first element whose key is >= key. 1002 | iterator lower_bound(const key_type &key) 1003 | { 1004 | if (_size == 0) 1005 | return end(); 1006 | auto loc = _root->find_ge(key); 1007 | return _iter(loc); 1008 | } 1009 | 1010 | const_iterator lower_bound(const key_type &key) const 1011 | { 1012 | return const_cast(this)->lower_bound(key); 1013 | } 1014 | 1015 | // Finds the first element whose key is > key. 1016 | iterator upper_bound(const key_type &key) 1017 | { 1018 | if (_size == 0) 1019 | return end(); 1020 | auto loc = _root->find_ge(key); 1021 | if (loc._group && loc._group->get_key(loc._idx) == key) 1022 | { 1023 | auto next_loc = loc._group->next(loc._idx); 1024 | return _iter(next_loc); 1025 | } 1026 | return _iter(loc); 1027 | } 1028 | 1029 | const_iterator upper_bound(const key_type &key) const 1030 | { 1031 | return const_cast(this)->upper_bound(key); 1032 | } 1033 | 1034 | 1035 | 1036 | // --------------------- apis ------------------------------------------ 1037 | bool empty() const { return !size(); } 1038 | size_type size() const { return _size; } 1039 | size_type capacity() const { return max_size(); } 1040 | size_type max_size() const { return (std::numeric_limits::max)(); } 1041 | 1042 | void clear() 1043 | { 1044 | if (size()) 1045 | { 1046 | _cleanup(); 1047 | _init(); 1048 | } 1049 | } 1050 | 1051 | // ------------------------------------ erase ------------------------------ 1052 | void _erase(iterator it) // use this when you don't need the iterator to be incremented 1053 | { 1054 | assert(it != end() && size() > 0); 1055 | if (it._group->erase(it._idx)) 1056 | { 1057 | // a leaf group was freed 1058 | _last = nullptr; 1059 | } 1060 | --_size; 1061 | } 1062 | 1063 | void _erase(const_iterator cit) 1064 | { 1065 | _erase(cit._it); 1066 | } 1067 | 1068 | iterator erase(const_iterator cit) 1069 | { 1070 | return erase(cit._it_); 1071 | } 1072 | 1073 | iterator erase(iterator it) 1074 | { 1075 | _erase(it++); 1076 | return it; 1077 | } 1078 | 1079 | size_type erase(const key_type& key) 1080 | { 1081 | auto it = find(key); 1082 | if (it == end()) 1083 | return 0; 1084 | _erase(it); 1085 | return 1; 1086 | } 1087 | 1088 | iterator erase(const_iterator first, const_iterator last) 1089 | { 1090 | while (first != last) 1091 | _erase(first++); 1092 | return last._it; 1093 | } 1094 | 1095 | // ------------------------------------ insert ------------------------------ 1096 | template 1097 | std::pair insert(VT&& value) 1098 | { 1099 | auto loc = insert_impl(value.first); 1100 | if (loc._created) 1101 | reinterpret_cast(loc._group)->set_value(loc._idx, std::forward(value.second)); 1102 | return { iterator(loc), loc._created }; 1103 | } 1104 | 1105 | template 1106 | iterator insert(const_iterator hint, VT&& value) 1107 | { 1108 | return insert(std::forward(value)).first; 1109 | } 1110 | 1111 | template 1112 | void insert(InputIt first, InputIt last) 1113 | { 1114 | for (; first != last; ++first) 1115 | insert(*first); 1116 | } 1117 | 1118 | void insert(std::initializer_list ilist) 1119 | { 1120 | insert(ilist.begin(), ilist.end()); 1121 | } 1122 | 1123 | template 1124 | std::pair emplace(Args&&... args) 1125 | { 1126 | return insert(value_type(std::forward(args)...)); 1127 | } 1128 | 1129 | template 1130 | iterator emplace_hint(const_iterator hint, Args&&... args) 1131 | { 1132 | return insert(hint, value_type(std::forward(args)...)); 1133 | } 1134 | 1135 | template 1136 | std::pair try_emplace(Key&& key, Args&&... args) 1137 | { 1138 | auto loc = insert_impl(key, std::piecewise_construct, 1139 | std::forward_as_tuple(std::forward(key))); 1140 | if (loc._created) 1141 | reinterpret_cast(loc._group)->set_value(loc._idx, std::forward_as_tuple(std::forward(args)...)); 1142 | return { iterator(loc), loc._created }; 1143 | } 1144 | 1145 | template 1146 | iterator try_emplace(const_iterator hint, Key&& key, Args&&... args) 1147 | { 1148 | return try_emplace(std::forward(key), std::forward(args)...).first; 1149 | } 1150 | 1151 | 1152 | // ------------------------------------ insert_or_assign ------------------------------ 1153 | template 1154 | std::pair insert_or_assign(key_type&& k, M&& v) 1155 | { 1156 | return insert_or_assign_impl(std::forward(k), std::forward(v)); 1157 | } 1158 | 1159 | template 1160 | std::pair insert_or_assign(key_type&& k, const M& v) 1161 | { 1162 | return insert_or_assign_impl(std::forward(k), v); 1163 | } 1164 | 1165 | template 1166 | std::pair insert_or_assign(const key_type& k, M&& v) 1167 | { 1168 | return insert_or_assign_impl(k, std::forward(v)); 1169 | } 1170 | 1171 | template 1172 | std::pair insert_or_assign(const key_type& k, const M& v) 1173 | { 1174 | return insert_or_assign_impl(k, v); 1175 | } 1176 | 1177 | template 1178 | iterator insert_or_assign(const_iterator, key_type&& k, M&& v) 1179 | { 1180 | return insert_or_assign(std::forward(k), std::forward(v)).first; 1181 | } 1182 | 1183 | template 1184 | iterator insert_or_assign(const_iterator, key_type&& k, const M& v) 1185 | { 1186 | return insert_or_assign(std::forward(k), v).first; 1187 | } 1188 | 1189 | template 1190 | iterator insert_or_assign(const_iterator, const key_type& k, M&& v) 1191 | { 1192 | return insert_or_assign(k, std::forward(v)).first; 1193 | } 1194 | 1195 | template 1196 | iterator insert_or_assign(const_iterator, const key_type& k, const M& v) 1197 | { 1198 | return insert_or_assign(k, v).first; 1199 | } 1200 | 1201 | #if 0 1202 | extract; 1203 | merge; 1204 | #endif 1205 | 1206 | void reserve(size_type count) {} 1207 | void rehash(size_type count) {} 1208 | 1209 | void swap(amt& o) noexcept 1210 | { 1211 | std::swap(_root, o._root); 1212 | std::swap(_last, o._last); 1213 | std::swap(_size, o._size); 1214 | } 1215 | 1216 | V& at(const key_type& key) 1217 | { 1218 | auto loc = this->find_impl(key); 1219 | if (!loc._group) 1220 | throw std::out_of_range("amt at(): lookup non-existent key"); 1221 | return reinterpret_cast(loc._group)->get_value(loc._idx);; 1222 | } 1223 | 1224 | const V& at(const key_type& key) const 1225 | { 1226 | return const_cast(this)->at(key); 1227 | } 1228 | 1229 | bool contains(const key_type& key) const 1230 | { 1231 | return this->find(key) != this->end(); 1232 | } 1233 | 1234 | size_type count(const key_type& key) const 1235 | { 1236 | return this->contains(key) ? 1 : 0; 1237 | } 1238 | 1239 | std::pair equal_range(const key_type& key) 1240 | { 1241 | auto it = find(key); 1242 | if (it != end()) 1243 | return {it, std::next(it)}; 1244 | return {it, it}; 1245 | } 1246 | 1247 | std::pair equal_range(const key_type& key) const 1248 | { 1249 | auto it = find(key); 1250 | if (it != end()) 1251 | return {it, std::next(it)}; 1252 | return {it, it}; 1253 | } 1254 | 1255 | iterator find(const key_type& key) 1256 | { 1257 | auto loc = find_impl(key); 1258 | return _iter(loc); 1259 | } 1260 | 1261 | const_iterator find(const key_type& key) const 1262 | { 1263 | return const_cast(this)->find(key); 1264 | } 1265 | 1266 | mapped_type& operator[](const key_type& key) 1267 | { 1268 | auto loc = insert_impl(key); 1269 | return reinterpret_cast(loc._group)->get_value(loc._idx); 1270 | } 1271 | 1272 | size_type bucket_count() const { return _size; } // not meaningful for amt 1273 | float load_factor() const { return 0.5f; } // not meaningful for amt 1274 | float max_load_factor() const { return 1.0f; } // not meaningful for amt 1275 | void max_load_factor(float) {} // not meaningful for amt 1276 | 1277 | key_equal key_eq() const { return std::equal_to(); } 1278 | 1279 | private: 1280 | iterator _iter(const locator &loc) 1281 | { 1282 | return loc._group ? iterator(loc) : end(); 1283 | } 1284 | 1285 | void _init() 1286 | { 1287 | assert(_root == nullptr); 1288 | _root = group::allocate_group(16, nullptr, 0, 0); 1289 | } 1290 | 1291 | void _cleanup() 1292 | { 1293 | _root->deallocate_group(); 1294 | _root = nullptr; 1295 | _last = nullptr; 1296 | _size = 0; 1297 | } 1298 | 1299 | locator find_impl(const key_type& k) 1300 | { 1301 | bool last_matches = _last && _last->match(k); 1302 | if (last_matches) 1303 | return _last->find(k); 1304 | auto loc = _root->find(k); 1305 | _last = loc._group; 1306 | assert(!_last || _last->match(k)); 1307 | return loc; 1308 | } 1309 | 1310 | insert_locator insert_impl(const key_type& k) 1311 | { 1312 | bool last_matches = _last && _last->match(k); 1313 | 1314 | if (last_matches) 1315 | { 1316 | auto loc = _last->find_or_prepare_insert(k); 1317 | _size += (size_type)loc._created; 1318 | _last = loc._group; // in case _last was resized (after a shrink) 1319 | return loc; 1320 | } 1321 | else 1322 | { 1323 | auto loc = _root->find_or_prepare_insert(k); 1324 | _size += (size_type)loc._created; 1325 | 1326 | if (_last) 1327 | _last->shrink(); 1328 | _last = loc._group; 1329 | assert(!_last || _last->match(k)); 1330 | return loc; 1331 | } 1332 | } 1333 | 1334 | friend class iterator; 1335 | 1336 | group_ptr _last; // always a leaf if set 1337 | group_ptr _root; // never a leaf 1338 | size_type _size; 1339 | }; 1340 | 1341 | 1342 | 1343 | } // namespace amt 1344 | 1345 | #ifdef _MSC_VER 1346 | #pragma warning(pop) 1347 | #endif 1348 | 1349 | 1350 | #endif // amt_h_guard_ 1351 | -------------------------------------------------------------------------------- /test/lower_bound.cpp: -------------------------------------------------------------------------------- 1 | // compile with: CL -IC:/greg/github/cpp-btree -O2 lower_bound.cpp 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include "../amt/amt.h" 10 | 11 | 12 | using std::vector; 13 | 14 | // -------------------------------------------------------------------------- 15 | template 16 | T myrand() 17 | { 18 | return ((T)rand() << 13) ^ (T)rand(); 19 | } 20 | 21 | // -------------------------------------------------------------------------- 22 | template 23 | void _fill(vector &v) 24 | { 25 | srand(1); // for a fair/deterministic comparison 26 | for (size_t i = 0, sz = v.size(); i < sz; ++i) 27 | v[i] = myrand(); 28 | } 29 | 30 | // -------------------------------------------------------------------------- 31 | template 32 | void _shuffle(vector &v) 33 | { 34 | for (size_t n = v.size(); n >= 2; --n) 35 | std::swap(v[n - 1], v[static_cast(myrand()) % n]); 36 | } 37 | 38 | // -------------------------------------------------------------------------- 39 | template 40 | void pr(const M &m) 41 | { 42 | printf("["); 43 | for (const auto& x : m) 44 | printf("%d, ", x.first); 45 | printf("]\n"); 46 | } 47 | 48 | // -------------------------------------------------------------------------- 49 | template 50 | void test(const M1 &m1, const M2 &m2) 51 | { 52 | // m2 is the reference map 53 | if (m2.empty()) 54 | return; 55 | 56 | auto it = m2.end(); 57 | --it; 58 | auto max = it->first; 59 | 60 | for (uint32_t i=0; ifirst == m2.lower_bound(i)->first; 63 | bool ub = m1.upper_bound(i)->first == m2.upper_bound(i)->first; 64 | if (!(lb && ub)) 65 | pr(m1); 66 | assert(lb && ub); 67 | } 68 | } 69 | 70 | // -------------------------------------------------------------------------- 71 | int main() 72 | { 73 | using M1 = amt::amt; 74 | using M2 = btree::btree_map; 75 | 76 | M1 m1; 77 | M2 m2; 78 | 79 | for (uint32_t j=2; j<100; ++j) 80 | { 81 | m1.clear(); 82 | m2.clear(); 83 | 84 | for (uint32_t i=1; i() % (j * 10); 87 | m1.insert(typename M1::value_type(x, x)); 88 | m2.insert(typename M2::value_type(x, x)); 89 | } 90 | test(m1, m2); 91 | } 92 | }; 93 | 94 | --------------------------------------------------------------------------------