| 1 | |
|---|
| 2 | |
|---|
| 3 | |
|---|
| 4 | #include <iostream> |
|---|
| 5 | #include <cassert> |
|---|
| 6 | #include <vector> |
|---|
| 7 | #include <algorithm> |
|---|
| 8 | #include <stdexcept> |
|---|
| 9 | #include <cstring> |
|---|
| 10 | |
|---|
| 11 | #include "boost/mpl/if.hpp" |
|---|
| 12 | #include "boost/utility/enable_if.hpp" |
|---|
| 13 | |
|---|
| 14 | namespace iwear |
|---|
| 15 | { |
|---|
| 16 | |
|---|
| 17 | template<class T, size_t N> |
|---|
| 18 | /*constexpr*/ size_t array_size( T(& )[N] ) |
|---|
| 19 | { |
|---|
| 20 | return N; |
|---|
| 21 | } |
|---|
| 22 | |
|---|
| 23 | #define asize(x) (sizeof(x) / sizeof(x[0])) |
|---|
| 24 | /* |
|---|
| 25 | * This class aims to be a quite versatile hashtable, where you can control |
|---|
| 26 | * various parameters of its behaviour via template parameters. |
|---|
| 27 | */ |
|---|
| 28 | |
|---|
| 29 | /** |
|---|
| 30 | * These enums control basic behaviours of the hashing functions built in. If |
|---|
| 31 | * you need something else for the trivial types, you need to roll your own |
|---|
| 32 | * structure that does this. |
|---|
| 33 | * @note You can create specializations for various other hashing classes |
|---|
| 34 | * yourself, and specify them at the same point where you instantiate them |
|---|
| 35 | * otherwise. To do that, simply specialize for other numerical values, like |
|---|
| 36 | * hash<T,(hashclass)3> and use that type wherever you want. |
|---|
| 37 | */ |
|---|
| 38 | enum class hashclass : int |
|---|
| 39 | { |
|---|
| 40 | identity, ///< If possible, use the value as the result of the hash, to suppress possible errors we static_cast to size_type |
|---|
| 41 | trivial, ///< Use a trivial hashing, like multiplication with a large prime |
|---|
| 42 | fnv ///< Use a hashing scheme similar to fnv (but not quite exactly) |
|---|
| 43 | }; |
|---|
| 44 | |
|---|
| 45 | /** |
|---|
| 46 | * This is a value metafunction that takes an integer as input and outputs the |
|---|
| 47 | * same value of the type hashclass. Whenever the input could be something |
|---|
| 48 | * convertible to int, this is a wrapper for the case you don't like casts. You |
|---|
| 49 | * can use it like make_hashclass<5>::value instead of (hash_class)5 |
|---|
| 50 | */ |
|---|
| 51 | template<int hc> |
|---|
| 52 | struct make_hashclass |
|---|
| 53 | { |
|---|
| 54 | static const hashclass value = (hashclass)hc; |
|---|
| 55 | }; |
|---|
| 56 | |
|---|
| 57 | /** |
|---|
| 58 | * This class is for the actual hashing. The user is supposed to specialize it |
|---|
| 59 | * for his own types in order to get the hashing behaviour he wants. It might |
|---|
| 60 | * be possible though, that for some reasons another hashing is needed for the |
|---|
| 61 | * same datatype. To compensate for this we allow for specifying the type of |
|---|
| 62 | * the hash struct later in the hash_table so that a user can use its own |
|---|
| 63 | * there. |
|---|
| 64 | * @note per default a trivial hashing is used, which is often a good balance |
|---|
| 65 | * between speed and degradation effects. |
|---|
| 66 | * @note We will always use this type without explicitly specifying its |
|---|
| 67 | * namespace. Therefore it could be possible that sometimes something from your |
|---|
| 68 | * namespace is picked up, be aware of that. |
|---|
| 69 | * @warning Various hashes have different degradation properties, like |
|---|
| 70 | * avalanche effects and similar. Consider carefully what you chose here, and |
|---|
| 71 | * in that consideration, think about who controls the data. If you control the |
|---|
| 72 | * data you can usually be much more lax with the requirements as you can make |
|---|
| 73 | * sure that no strange clustering effects can occur. |
|---|
| 74 | */ |
|---|
| 75 | template<class T, hashclass HC > |
|---|
| 76 | struct hash |
|---|
| 77 | { |
|---|
| 78 | typedef size_t size_type; |
|---|
| 79 | typedef T item_type; |
|---|
| 80 | private: |
|---|
| 81 | /** |
|---|
| 82 | * If you get a compile error here saying that this is private, you need to |
|---|
| 83 | * specialize your own version of hash for your item_type with this |
|---|
| 84 | * function not being private. This generic version is just to communicate |
|---|
| 85 | * a defined interface. |
|---|
| 86 | */ |
|---|
| 87 | size_type operator()( const item_type& i ); |
|---|
| 88 | size_type operator()( item_type* i ); |
|---|
| 89 | }; |
|---|
| 90 | |
|---|
| 91 | namespace __detail |
|---|
| 92 | { |
|---|
| 93 | |
|---|
| 94 | template<class size_type, int N> |
|---|
| 95 | struct roundup |
|---|
| 96 | { |
|---|
| 97 | static size_type roundup_recurse( size_type v ) |
|---|
| 98 | { |
|---|
| 99 | v = roundup<size_type,N/2>::roundup_recurse(v); |
|---|
| 100 | v |= v >> N; |
|---|
| 101 | return v; |
|---|
| 102 | } |
|---|
| 103 | }; |
|---|
| 104 | |
|---|
| 105 | template<class size_type> |
|---|
| 106 | struct roundup<size_type,1> |
|---|
| 107 | { |
|---|
| 108 | static size_type roundup_recurse( size_type v ) |
|---|
| 109 | { |
|---|
| 110 | v |= v >> 1; |
|---|
| 111 | return v; |
|---|
| 112 | } |
|---|
| 113 | }; |
|---|
| 114 | |
|---|
| 115 | template<class size_type> |
|---|
| 116 | size_type roundup2( size_type v ) |
|---|
| 117 | { |
|---|
| 118 | v--; |
|---|
| 119 | v = roundup<size_type,sizeof(v)>::roundup_recurse(v); |
|---|
| 120 | v++; |
|---|
| 121 | return v; |
|---|
| 122 | } |
|---|
| 123 | |
|---|
| 124 | } // namespace __detail |
|---|
| 125 | |
|---|
| 126 | #if 0 // old shit ;) |
|---|
| 127 | /** |
|---|
| 128 | * This class controls how the sizes of the hashtable are selected, as well as |
|---|
| 129 | * how the hash value is actually transformed into an index. |
|---|
| 130 | * @param binary decides whether the hashtable uses power of two sizes only, |
|---|
| 131 | * and then the operator& to determine indices or whether a predefined list of |
|---|
| 132 | * primes is used with the operator%. |
|---|
| 133 | */ |
|---|
| 134 | template<bool binary > |
|---|
| 135 | struct hash_selection |
|---|
| 136 | { |
|---|
| 137 | typedef size_t size_type; |
|---|
| 138 | |
|---|
| 139 | /** |
|---|
| 140 | * With this we control whether the hashtable will automatically resize |
|---|
| 141 | * when it detects after an insert that it is now too small, or whether the |
|---|
| 142 | * user is responsible for calling the corresponding check function. |
|---|
| 143 | * @note that in no case a check for the size is done, when the slot in the |
|---|
| 144 | * hashtable that was found wasn't already occupied. |
|---|
| 145 | */ |
|---|
| 146 | static const bool auto_resize; |
|---|
| 147 | /** |
|---|
| 148 | * required helper function that will determine the type of the pointer |
|---|
| 149 | * being used. You can write your own variant, that will use e.g. |
|---|
| 150 | * boost::offset_ptr so all pointers that are permanently stored in memory |
|---|
| 151 | * are of such a type which means you can store things in shared memory or |
|---|
| 152 | * in a memory mapped file you will be able to relocate. |
|---|
| 153 | */ |
|---|
| 154 | template<class T> |
|---|
| 155 | struct add_pointer |
|---|
| 156 | { |
|---|
| 157 | typedef T* type; |
|---|
| 158 | }; |
|---|
| 159 | |
|---|
| 160 | /** |
|---|
| 161 | * The default metafunction for creating the slot item type assumes that we |
|---|
| 162 | * are using a singly linked list for our hashtable. |
|---|
| 163 | */ |
|---|
| 164 | template<class T> |
|---|
| 165 | struct slot_item |
|---|
| 166 | { |
|---|
| 167 | typedef typename add_pointer<T>::type type; |
|---|
| 168 | }; |
|---|
| 169 | |
|---|
| 170 | /** |
|---|
| 171 | * Transforms the hash into an actual index. |
|---|
| 172 | * @param hash is the hash as gathered from the hash structure |
|---|
| 173 | * @param size is the current size of the hashtable in number of slots |
|---|
| 174 | */ |
|---|
| 175 | |
|---|
| 176 | static size_type index( size_type hash, size_type size ); |
|---|
| 177 | |
|---|
| 178 | /** |
|---|
| 179 | * @returns true if a resize of the hashtable is recommended, making the |
|---|
| 180 | * hashtable actually bigger. If a shrink is possible, this does not mean a |
|---|
| 181 | * resize is recommended, so this function returns false. |
|---|
| 182 | */ |
|---|
| 183 | static bool need_resize( size_type size, size_type filled ); |
|---|
| 184 | |
|---|
| 185 | /** |
|---|
| 186 | Do a resize so the size vs filled ratio is optimized in case too much |
|---|
| 187 | * slots are filled. If there are already enough free slots, no change is |
|---|
| 188 | * made. |
|---|
| 189 | * @returns true iff an actual resize was made. |
|---|
| 190 | */ |
|---|
| 191 | static bool resize( size_type size, size_type filled ); |
|---|
| 192 | |
|---|
| 193 | /** |
|---|
| 194 | * Shrinks a hashtable to the least minimum necessary for a proper |
|---|
| 195 | * fillratio of the table. This might be significantly slower as the |
|---|
| 196 | * recommended size used in resize() so that you should only ever call this |
|---|
| 197 | * if you think the hashtable will not be filled in the future. |
|---|
| 198 | */ |
|---|
| 199 | static bool shrink( size_type size, size_type filled ); |
|---|
| 200 | }; |
|---|
| 201 | |
|---|
| 202 | template<> |
|---|
| 203 | struct hash_selection<false> |
|---|
| 204 | { |
|---|
| 205 | template<class R> |
|---|
| 206 | struct add_pointer |
|---|
| 207 | { |
|---|
| 208 | typedef R* type; |
|---|
| 209 | }; |
|---|
| 210 | |
|---|
| 211 | template<class R> |
|---|
| 212 | struct slot_item |
|---|
| 213 | { |
|---|
| 214 | typedef typename add_pointer<R>::type type; |
|---|
| 215 | }; |
|---|
| 216 | |
|---|
| 217 | typedef size_t size_type; |
|---|
| 218 | static size_type index( size_type hash, size_type size ) |
|---|
| 219 | { |
|---|
| 220 | #ifdef DEBUG |
|---|
| 221 | std::cerr << "hash = " << hash << ", size = " << size << "\n"; |
|---|
| 222 | #endif |
|---|
| 223 | return hash % size; |
|---|
| 224 | } |
|---|
| 225 | }; |
|---|
| 226 | #endif |
|---|
| 227 | |
|---|
| 228 | struct slot_selector_modulo |
|---|
| 229 | { |
|---|
| 230 | static size_t mask_from_length( size_t len ) |
|---|
| 231 | { |
|---|
| 232 | return len; |
|---|
| 233 | } |
|---|
| 234 | |
|---|
| 235 | size_t operator()( size_t hsum, size_t mask ) const |
|---|
| 236 | { |
|---|
| 237 | #ifdef DEBUG |
|---|
| 238 | std::cerr << "slot_selector_modulo()(" << hsum << " % " << mask << " == " << (hsum % mask) << "\n"; |
|---|
| 239 | #endif |
|---|
| 240 | return hsum % mask; |
|---|
| 241 | } |
|---|
| 242 | }; |
|---|
| 243 | |
|---|
| 244 | struct slot_selector_power2 |
|---|
| 245 | { |
|---|
| 246 | static size_t mask_from_length( size_t len ) |
|---|
| 247 | { |
|---|
| 248 | return len - 1; |
|---|
| 249 | } |
|---|
| 250 | |
|---|
| 251 | size_t operator()( size_t hsum, size_t mask ) const |
|---|
| 252 | { |
|---|
| 253 | #ifdef DEBUG |
|---|
| 254 | std::cerr << "slot_selector_power2()(" << hsum << " & " << mask << " == " << (hsum & mask) << "\n"; |
|---|
| 255 | #endif |
|---|
| 256 | // assert( mask+1 is power of 2 ); |
|---|
| 257 | return hsum & mask; |
|---|
| 258 | } |
|---|
| 259 | }; |
|---|
| 260 | |
|---|
| 261 | |
|---|
| 262 | // ############################################# # # # # # # # # # # # # # # # # # # # # # # # # |
|---|
| 263 | // |
|---|
| 264 | // for the factor 1.1 |
|---|
| 265 | size_t primetable11[] = |
|---|
| 266 | { |
|---|
| 267 | 2, 3, 5, 7, 11, 13, |
|---|
| 268 | 17, 19, 23, 29, 31, 37, |
|---|
| 269 | 41, 43, 53, 59, 67, 73, |
|---|
| 270 | 83, 89, 97, 107, 127, 139, |
|---|
| 271 | 157, 167, 191, 211, 223, 251, |
|---|
| 272 | 269, 293, 331, 359, 389, 431, |
|---|
| 273 | 479, 521, 569, 631, 691, 757, |
|---|
| 274 | 827, 911, 1009, 1103, 1213, 1327, |
|---|
| 275 | 1459, 1607, 1777, 1949, 2137, 2347, |
|---|
| 276 | 2591, 2843, 3137, 3449, 3779, 4157, |
|---|
| 277 | 4583, 5039, 5527, 6079, 6689, 7369, |
|---|
| 278 | 8089, 8923, 9787, 10771, 11863, 13033, |
|---|
| 279 | 14327, 15761, 17333, 19069, 20981, 23071, |
|---|
| 280 | 25391, 27917, 30703, 33773, 37159, 40867, |
|---|
| 281 | 44953, 49451, 54401, 59833, 65827, 72421, |
|---|
| 282 | 79631, 87613, 96353, 105997, 116593, 128257, |
|---|
| 283 | 141073, 155191, 170701, 187763, 206543, 227189, |
|---|
| 284 | 249911, 274909, 302399, 332641, 365903, 402487, |
|---|
| 285 | 442721, 487007, 535697, 589273, 648191, 713021, |
|---|
| 286 | 784307, 862739, 949019, 1043921, 1148311, 1263133, |
|---|
| 287 | 1389439, 1528399, 1681241, 1849349, 2034283, 2237743, |
|---|
| 288 | 2461477, 2707651, 2978399, 3276241, 3603857, 3964229, |
|---|
| 289 | 4360649, 4796749, 5276387, 5804023, 6384439, 7022867, |
|---|
| 290 | 7725161, 8497681, 9347441, 10282177, 11310413, 12441439, |
|---|
| 291 | 13685579, 15054157, 16559561, 18215501, 20037053, 22040771, |
|---|
| 292 | 24244837, 26669317, 29336243, 32269877, 35496869, 39046541, |
|---|
| 293 | 42951203, 47246359, 51970957, 57168043, 62884859, 69173333, |
|---|
| 294 | 76090681, 83699761, 92069729, 101276699, 111404369, 122544787, |
|---|
| 295 | 134799271, 148279217, 163107157, 179417839, 197359663, 217095589, |
|---|
| 296 | 238805207, 262685693, 288954269, 317849681, 349634651, 384598153, |
|---|
| 297 | 423057949, 465363781, 511900133, 563090189, 619399213, 681339121, |
|---|
| 298 | 749473051, 824420407, 906862427, 997548689, 1097303609, 1207033967, |
|---|
| 299 | 1327737391, 1460511163, 1606562357, 1767218597, 1943940541, 2138334589, |
|---|
| 300 | 2352168083UL, 2587384981UL, 2846123519UL, 3130735931UL, 3443809657UL, 3788190661UL, |
|---|
| 301 | 4167009773UL, |
|---|
| 302 | }; |
|---|
| 303 | |
|---|
| 304 | // for the factor 1.5 |
|---|
| 305 | size_t primetable15[] = |
|---|
| 306 | { |
|---|
| 307 | 2, 5, 7, 11, 17, 29, |
|---|
| 308 | 41, 59, 89, 137, 199, 307, |
|---|
| 309 | 449, 673, 1009, 1511, 2267, 3391, |
|---|
| 310 | 5087, 7639, 11443, 17167, 25741, 38611, |
|---|
| 311 | 57917, 86923, 130337, 195469, 293201, 439799, |
|---|
| 312 | 659689, 989533, 1484291, 2226431, 3339643, 5009471, |
|---|
| 313 | 7514231, 11271319, 16906949, 25360441, 38040619, 57060931, |
|---|
| 314 | 85591417, 128387137, 192580621, 288870937, 433306427, 649959679, |
|---|
| 315 | 974939417, 1462409083, 2193613607UL, 3290420401UL |
|---|
| 316 | }; |
|---|
| 317 | |
|---|
| 318 | template< |
|---|
| 319 | int grow_load_pm, |
|---|
| 320 | int perfect_load_pm, |
|---|
| 321 | int shrink_load_pm |
|---|
| 322 | > |
|---|
| 323 | struct resize_base |
|---|
| 324 | { |
|---|
| 325 | // XXX one day take some time and think about whether we can safely do all this |
|---|
| 326 | // with integer only maths. Or even provide a template parameter for this. |
|---|
| 327 | static const float shrink_load = shrink_load_pm / 1000.0f; |
|---|
| 328 | static const float perfect_load = perfect_load_pm / 1000.0f; |
|---|
| 329 | static const float grow_load = grow_load_pm / 1000.0f; |
|---|
| 330 | |
|---|
| 331 | /** |
|---|
| 332 | */ |
|---|
| 333 | // __attribute__((noinline)) |
|---|
| 334 | static bool need_grow( size_t elements, size_t slots ) |
|---|
| 335 | { |
|---|
| 336 | // XXX A later design could sacrifice a few bytes to store the actual |
|---|
| 337 | // values in elements for grow and shrink barriers, so that a check |
|---|
| 338 | // against those only needs to compare against them. Of course in case |
|---|
| 339 | // of a real resize event those values will need to be recalculated. |
|---|
| 340 | // Since this is a speed vs space trade-off we will provide template |
|---|
| 341 | // parameters for that with proper defaults. |
|---|
| 342 | |
|---|
| 343 | // calculate actual load factor |
|---|
| 344 | double lfactor = double(elements)/slots; |
|---|
| 345 | #ifdef DEBUG |
|---|
| 346 | std::cerr << "need_grow load factor " << lfactor << "\n"; |
|---|
| 347 | std::cerr << "load factor at which to start growing " << grow_load << "\n"; |
|---|
| 348 | std::cerr << "returning " << ( lfactor >= grow_load ) << "\n"; |
|---|
| 349 | #endif |
|---|
| 350 | if( lfactor >= grow_load ) |
|---|
| 351 | { |
|---|
| 352 | return true; |
|---|
| 353 | } |
|---|
| 354 | else |
|---|
| 355 | { |
|---|
| 356 | return false; |
|---|
| 357 | } |
|---|
| 358 | } |
|---|
| 359 | |
|---|
| 360 | static bool need_shrink( size_t elements, size_t slots ) |
|---|
| 361 | { |
|---|
| 362 | // calculate actual load factor |
|---|
| 363 | // XXX Check whether the compiler can properly inline and elide this |
|---|
| 364 | // calculation in the case need_resize() will be called. |
|---|
| 365 | double lfactor = double(elements)/slots; |
|---|
| 366 | #ifdef DEBUG |
|---|
| 367 | std::cerr << "need_shrink load factor " << lfactor << "\n"; |
|---|
| 368 | std::cerr << "load factor at which to start shrinking " << shrink_load << "\n"; |
|---|
| 369 | std::cerr << "returning " << ( lfactor < shrink_load ) << "\n"; |
|---|
| 370 | #endif |
|---|
| 371 | if( lfactor < shrink_load ) |
|---|
| 372 | { |
|---|
| 373 | return true; |
|---|
| 374 | } |
|---|
| 375 | else |
|---|
| 376 | { |
|---|
| 377 | return false; |
|---|
| 378 | } |
|---|
| 379 | } |
|---|
| 380 | |
|---|
| 381 | static bool need_resize( size_t elements, size_t slots ) |
|---|
| 382 | { |
|---|
| 383 | return need_grow(elements,slots) || need_shrink(elements,slots); |
|---|
| 384 | } |
|---|
| 385 | |
|---|
| 386 | }; |
|---|
| 387 | |
|---|
| 388 | template< |
|---|
| 389 | int grow_load_pm, |
|---|
| 390 | int perfect_load_pm, |
|---|
| 391 | int shrink_load_pm, |
|---|
| 392 | size_t* prime_table_start, |
|---|
| 393 | size_t prime_table_size |
|---|
| 394 | > |
|---|
| 395 | struct resize_primetable_template |
|---|
| 396 | : resize_base<grow_load_pm,perfect_load_pm, shrink_load_pm> |
|---|
| 397 | { |
|---|
| 398 | typedef resize_base<grow_load_pm,perfect_load_pm, shrink_load_pm> base_type; |
|---|
| 399 | |
|---|
| 400 | typedef slot_selector_modulo slot_selector; |
|---|
| 401 | |
|---|
| 402 | static size_t max_size( ) |
|---|
| 403 | { |
|---|
| 404 | return prime_table_start[prime_table_size-1]; |
|---|
| 405 | } |
|---|
| 406 | /** |
|---|
| 407 | * The optimal size will be calculated in the way that from the desired |
|---|
| 408 | * load factor (per default 0.8*elements) on we will take the next bigger |
|---|
| 409 | * prime from our table. The table was generated using a start number (89) |
|---|
| 410 | * and multiplying this by 1.5 and then choosing the next prime (and so on, |
|---|
| 411 | * taking the previous result and not the prime as input for multiplication |
|---|
| 412 | * by 1.5 so that you can easier determine the numbers). |
|---|
| 413 | * @note the optimal_size function is potentially expensive and should only |
|---|
| 414 | * be called in the case a resize needs to be done. Determining whether a |
|---|
| 415 | * resize is needed should be done purely on the current sizes load factor. |
|---|
| 416 | */ |
|---|
| 417 | static size_t optimal_size( size_t elements ) |
|---|
| 418 | { |
|---|
| 419 | // First of all we calculate the number of slots for the desired |
|---|
| 420 | // perfect load factor. |
|---|
| 421 | size_t min_slots = elements / base_type::perfect_load; |
|---|
| 422 | // then search in the provided primetable for a proper prime number. |
|---|
| 423 | size_t* i = std::lower_bound(prime_table_start, prime_table_start + prime_table_size , min_slots); |
|---|
| 424 | if( i == (prime_table_start+prime_table_size) ) |
|---|
| 425 | { |
|---|
| 426 | // XXX refine to a more suited exception type one day |
|---|
| 427 | throw std::runtime_error("Exceeded built in primetable while generating optimal_size"); |
|---|
| 428 | } |
|---|
| 429 | return *i; |
|---|
| 430 | } |
|---|
| 431 | |
|---|
| 432 | static bool need_resize( size_t elements, size_t slots ) |
|---|
| 433 | { |
|---|
| 434 | return (base_type::need_grow(elements,slots) || base_type::need_shrink(elements,slots)) |
|---|
| 435 | // actually ask for shrink only if the amount of elements is really |
|---|
| 436 | // bigger than the smallest size we would ever have. |
|---|
| 437 | && prime_table_start[0] < elements; |
|---|
| 438 | } |
|---|
| 439 | |
|---|
| 440 | |
|---|
| 441 | }; |
|---|
| 442 | |
|---|
| 443 | template< |
|---|
| 444 | int grow_load_pm = 800, |
|---|
| 445 | int perfect_load_pm = 700, |
|---|
| 446 | int shrink_load_pm = 300 |
|---|
| 447 | > |
|---|
| 448 | struct resize_primetable_15 |
|---|
| 449 | : resize_primetable_template<grow_load_pm,perfect_load_pm, shrink_load_pm,primetable15,asize(primetable15)> |
|---|
| 450 | { |
|---|
| 451 | }; |
|---|
| 452 | |
|---|
| 453 | |
|---|
| 454 | // XXX A good default load factor depends on the collision resolution used |
|---|
| 455 | template< |
|---|
| 456 | int grow_load_pm = 800, |
|---|
| 457 | int perfect_load_pm = 700, |
|---|
| 458 | int shrink_load_pm = 300 |
|---|
| 459 | > |
|---|
| 460 | struct resize_power2 |
|---|
| 461 | : resize_base<grow_load_pm, perfect_load_pm, shrink_load_pm> |
|---|
| 462 | { |
|---|
| 463 | typedef resize_base<grow_load_pm, perfect_load_pm, shrink_load_pm> base_type; |
|---|
| 464 | |
|---|
| 465 | typedef slot_selector_power2 slot_selector; |
|---|
| 466 | |
|---|
| 467 | static size_t max_size( ) |
|---|
| 468 | { |
|---|
| 469 | return static_cast<size_t>(-1); |
|---|
| 470 | } |
|---|
| 471 | /** |
|---|
| 472 | * |
|---|
| 473 | */ |
|---|
| 474 | static size_t optimal_size( size_t elements ) |
|---|
| 475 | { |
|---|
| 476 | // the optimal size is given a certain load factor the next power of |
|---|
| 477 | // two after the amount of slots needed to achieve this load factor. |
|---|
| 478 | size_t min_slots = elements / base_type::perfect_load; |
|---|
| 479 | // Now find the next power of two... |
|---|
| 480 | size_t s = __detail::roundup2(min_slots); |
|---|
| 481 | return s; |
|---|
| 482 | } |
|---|
| 483 | }; |
|---|
| 484 | |
|---|
| 485 | struct raw_pointer |
|---|
| 486 | { |
|---|
| 487 | template<class T> |
|---|
| 488 | struct pointer |
|---|
| 489 | { |
|---|
| 490 | typedef T* type; |
|---|
| 491 | }; |
|---|
| 492 | }; |
|---|
| 493 | |
|---|
| 494 | |
|---|
| 495 | |
|---|
| 496 | // generic anchor hierarchy, to be used by all possible containers that might |
|---|
| 497 | // want to use them. |
|---|
| 498 | |
|---|
| 499 | template<class T, class PTR_SELECTOR > |
|---|
| 500 | struct anchor_single |
|---|
| 501 | { |
|---|
| 502 | typedef T item_type; |
|---|
| 503 | typedef typename PTR_SELECTOR::template pointer<item_type>::type pointer_type; |
|---|
| 504 | typedef decltype(*pointer_type()) reference_type; |
|---|
| 505 | |
|---|
| 506 | static const bool empty = false; |
|---|
| 507 | protected: |
|---|
| 508 | pointer_type next; |
|---|
| 509 | public: |
|---|
| 510 | anchor_single( ) |
|---|
| 511 | : next(0) // not really necessary but gcc warns otherwise |
|---|
| 512 | { |
|---|
| 513 | #ifdef DEBUG |
|---|
| 514 | next = reinterpret_cast<pointer_type>(~0UL); |
|---|
| 515 | #endif |
|---|
| 516 | } |
|---|
| 517 | |
|---|
| 518 | const pointer_type get_next() const { return next; } |
|---|
| 519 | pointer_type get_next() { return next; } |
|---|
| 520 | void set_next( pointer_type ptr ) { next = ptr; } |
|---|
| 521 | }; |
|---|
| 522 | |
|---|
| 523 | template<class T, class PTR_SELECTOR> |
|---|
| 524 | struct anchor_double : public anchor_single<T,PTR_SELECTOR> |
|---|
| 525 | { |
|---|
| 526 | typedef anchor_single<T,PTR_SELECTOR> base_type; |
|---|
| 527 | typedef typename base_type::item_type item_type; |
|---|
| 528 | typedef typename base_type::pointer_type pointer_type; |
|---|
| 529 | typedef typename base_type::reference_type reference_type; |
|---|
| 530 | protected: |
|---|
| 531 | pointer_type prev; |
|---|
| 532 | public: |
|---|
| 533 | const pointer_type get_prev() const { return prev; } |
|---|
| 534 | void set_prev( pointer_type ptr ) { prev = ptr; } |
|---|
| 535 | }; |
|---|
| 536 | |
|---|
| 537 | template<class T, class PTR_SELECTOR> |
|---|
| 538 | struct anchor_btree : protected anchor_double<T,PTR_SELECTOR> |
|---|
| 539 | { |
|---|
| 540 | typedef anchor_double<T,PTR_SELECTOR> base_type; |
|---|
| 541 | typedef typename base_type::item_type item_type; |
|---|
| 542 | typedef typename base_type::pointer_type pointer_type; |
|---|
| 543 | typedef typename base_type::reference_type reference_type; |
|---|
| 544 | |
|---|
| 545 | const pointer_type get_left() const { return base_type::prev; } |
|---|
| 546 | const pointer_type get_right() const { return base_type::next; } |
|---|
| 547 | |
|---|
| 548 | void set_left( pointer_type ptr ) { base_type::set_prev(ptr); } |
|---|
| 549 | void set_right( pointer_type ptr ) { base_type::set_next(ptr); } |
|---|
| 550 | |
|---|
| 551 | base_type& get_base() { return *this; } |
|---|
| 552 | const base_type& get_base() const { return *this; } |
|---|
| 553 | }; |
|---|
| 554 | |
|---|
| 555 | namespace avl |
|---|
| 556 | { |
|---|
| 557 | enum class skew |
|---|
| 558 | { |
|---|
| 559 | none, |
|---|
| 560 | left, |
|---|
| 561 | right |
|---|
| 562 | }; |
|---|
| 563 | } |
|---|
| 564 | |
|---|
| 565 | /* need to be adapted to new PTR_SELECTOR non-template variant |
|---|
| 566 | template<class T, template<class> class PTR> |
|---|
| 567 | struct anchor_avltree : public anchor_btree<T,PTR> |
|---|
| 568 | { |
|---|
| 569 | avl::skew sk; |
|---|
| 570 | avl::skew get_skew() const { return sk; } |
|---|
| 571 | void set_skew( avl::skew _sk ) { sk = _sk; } |
|---|
| 572 | }; |
|---|
| 573 | |
|---|
| 574 | template<class T, template<class> class PTR> |
|---|
| 575 | struct anchor_compressed_avltree : public anchor_btree<T,PTR> |
|---|
| 576 | { |
|---|
| 577 | typedef anchor_btree<T,PTR> base_type; |
|---|
| 578 | typedef typename base_type::item_type item_type; |
|---|
| 579 | typedef typename base_type::pointer_type pointer_type; |
|---|
| 580 | typedef typename base_type::reference_type reference_type; |
|---|
| 581 | |
|---|
| 582 | // XXX its likely a good idea to have this as a free utility function somewhere |
|---|
| 583 | static ptrdiff_t get_pointer_bits( const void* ptr, ptrdiff_t mask ) |
|---|
| 584 | { |
|---|
| 585 | const char* t = reinterpret_cast<const char*>(ptr); |
|---|
| 586 | const char* null = 0; |
|---|
| 587 | ptrdiff_t d = t - null; |
|---|
| 588 | return (d&mask); |
|---|
| 589 | } |
|---|
| 590 | // abuse boost::mpl::print to emit a warning when sizeof(base) != sizeof(*this) |
|---|
| 591 | avl::skew get_skew() const |
|---|
| 592 | { |
|---|
| 593 | // for efficiency reasons, if possible we only use the left pointer |
|---|
| 594 | if( sizeof(*this) >= 4 ) |
|---|
| 595 | { |
|---|
| 596 | // pointer points always to a multiple of 4, meaning that the lower |
|---|
| 597 | // two bits are reserved for us. Useful compilers will eliminate |
|---|
| 598 | // this if. |
|---|
| 599 | return static_cast<avl::skew>( get_pointer_bits(get_left(), 0x3u)); |
|---|
| 600 | } |
|---|
| 601 | else |
|---|
| 602 | { |
|---|
| 603 | const ptrdiff_t mask = 0x1u; |
|---|
| 604 | assert( sizeof(*this) >= 2 ); // it cannot be lower, we consist of two distinct objects, that is pointers |
|---|
| 605 | if( get_pointer_bits(get_left(),mask) ) // lowest bit of left is set |
|---|
| 606 | { |
|---|
| 607 | assert( !get_pointer_bits(get_right() & mask) ); // both bits are never set by us, so this must be a bug somewhere |
|---|
| 608 | return avl::skew::left; |
|---|
| 609 | } |
|---|
| 610 | else if( get_pointer_bits(get_right(),mask) ) // lowest bit of left is set |
|---|
| 611 | { |
|---|
| 612 | assert( !get_pointer_bits(get_left() & mask) ); // both bits are never set by us, so this must be a bug somewhere |
|---|
| 613 | return avl::skew::right; |
|---|
| 614 | } |
|---|
| 615 | // no bit is set, skew is none |
|---|
| 616 | return avl::skew::none; |
|---|
| 617 | } |
|---|
| 618 | } |
|---|
| 619 | |
|---|
| 620 | void set_skew( avl::skew _sk ); |
|---|
| 621 | |
|---|
| 622 | // XXX its likely a good idea to have this as a free utility function somewhere |
|---|
| 623 | template<class U> |
|---|
| 624 | U* get_masked_pointer( const void* ptr, ptrdiff_t mask ) |
|---|
| 625 | { |
|---|
| 626 | U* ret; |
|---|
| 627 | const char* t = reinterpret_cast<const char*>(ptr); |
|---|
| 628 | const char* null = 0; |
|---|
| 629 | ptrdiff_t d = t - null; |
|---|
| 630 | d &= ~mask; // mask "off" the bits |
|---|
| 631 | ret = static_cast<U*>(null + d); |
|---|
| 632 | return ret; |
|---|
| 633 | } |
|---|
| 634 | |
|---|
| 635 | const pointer_type get_left() const |
|---|
| 636 | { |
|---|
| 637 | if( sizeof(*this) >= 4 ) |
|---|
| 638 | { |
|---|
| 639 | return get_masked_pointer<const pointer_type>(base_type::get_left(),0x3u); |
|---|
| 640 | } |
|---|
| 641 | else |
|---|
| 642 | { |
|---|
| 643 | return get_masked_pointer<const pointer_type>(base_type::get_left(),0x1u); |
|---|
| 644 | } |
|---|
| 645 | } |
|---|
| 646 | |
|---|
| 647 | const pointer_type get_right() const |
|---|
| 648 | { |
|---|
| 649 | if( sizeof(*this) >= 4 ) |
|---|
| 650 | { // in this case, the right pointer is not used for special bits |
|---|
| 651 | return base_type::get_right(); |
|---|
| 652 | } |
|---|
| 653 | else |
|---|
| 654 | { // in this case, its the lowest bit, mask it off |
|---|
| 655 | return get_masked_pointer<const pointer_type>(base_type::get_right(),0x1u); |
|---|
| 656 | } |
|---|
| 657 | } |
|---|
| 658 | |
|---|
| 659 | void set_left( pointer_type ptr ) |
|---|
| 660 | { |
|---|
| 661 | // XXX We should make this function act upon the actual skew and |
|---|
| 662 | // sizeof(*this) and maybe change the skew only if affected by the |
|---|
| 663 | // pointer or so. |
|---|
| 664 | avl::skew s = get_skew(); |
|---|
| 665 | base_type::set_left(ptr); |
|---|
| 666 | set_skew(s); |
|---|
| 667 | } |
|---|
| 668 | |
|---|
| 669 | void set_right( pointer_type ptr ) |
|---|
| 670 | { |
|---|
| 671 | avl::skew s = get_skew(); |
|---|
| 672 | base_type::set_right(ptr); |
|---|
| 673 | set_skew(s); |
|---|
| 674 | } |
|---|
| 675 | }; |
|---|
| 676 | */ |
|---|
| 677 | |
|---|
| 678 | enum class occupancy : unsigned char |
|---|
| 679 | { |
|---|
| 680 | free, |
|---|
| 681 | deleted, |
|---|
| 682 | occupied |
|---|
| 683 | }; |
|---|
| 684 | |
|---|
| 685 | template<bool> |
|---|
| 686 | struct anchor_occupancy_storage; |
|---|
| 687 | |
|---|
| 688 | template<> |
|---|
| 689 | struct anchor_occupancy_storage<true> |
|---|
| 690 | { |
|---|
| 691 | occupancy occupied; |
|---|
| 692 | }; |
|---|
| 693 | |
|---|
| 694 | template<> |
|---|
| 695 | struct anchor_occupancy_storage<false> |
|---|
| 696 | { |
|---|
| 697 | }; |
|---|
| 698 | |
|---|
| 699 | template<bool> |
|---|
| 700 | struct anchor_hash_storage; |
|---|
| 701 | |
|---|
| 702 | template<> |
|---|
| 703 | struct anchor_hash_storage<true> |
|---|
| 704 | { |
|---|
| 705 | size_t hash; |
|---|
| 706 | }; |
|---|
| 707 | |
|---|
| 708 | template<> |
|---|
| 709 | struct anchor_hash_storage<false> |
|---|
| 710 | { |
|---|
| 711 | }; |
|---|
| 712 | |
|---|
| 713 | template<class anchor_type, bool empty> |
|---|
| 714 | struct anchor_storage; |
|---|
| 715 | |
|---|
| 716 | template<class anchor_type> |
|---|
| 717 | struct anchor_storage<anchor_type,false> |
|---|
| 718 | { |
|---|
| 719 | anchor_type anchor; |
|---|
| 720 | }; |
|---|
| 721 | |
|---|
| 722 | template<class anchor_type> |
|---|
| 723 | struct anchor_storage<anchor_type,true> |
|---|
| 724 | { |
|---|
| 725 | }; |
|---|
| 726 | |
|---|
| 727 | template< class anchor_type, bool store_hash, bool store_occupancy> |
|---|
| 728 | struct anchor_member_mixer : |
|---|
| 729 | anchor_storage<anchor_type, anchor_type::empty>, |
|---|
| 730 | anchor_occupancy_storage<store_occupancy>, |
|---|
| 731 | anchor_hash_storage<store_hash> |
|---|
| 732 | |
|---|
| 733 | { |
|---|
| 734 | }; |
|---|
| 735 | |
|---|
| 736 | template<class ANCHOR_ACCESSOR> |
|---|
| 737 | struct single_inserter_template |
|---|
| 738 | { |
|---|
| 739 | typedef ANCHOR_ACCESSOR anchor_accessor; |
|---|
| 740 | |
|---|
| 741 | template<class AA> |
|---|
| 742 | struct rebind |
|---|
| 743 | { |
|---|
| 744 | typedef single_inserter_template<AA> other; |
|---|
| 745 | }; |
|---|
| 746 | |
|---|
| 747 | // XXX choose per template parameter whether to put things at the beginning |
|---|
| 748 | // or the end of the list |
|---|
| 749 | template< |
|---|
| 750 | class IT, |
|---|
| 751 | class SZ, |
|---|
| 752 | class SLA |
|---|
| 753 | > |
|---|
| 754 | static bool insert( IT* i, SZ idx, SLA** sa ) |
|---|
| 755 | { |
|---|
| 756 | SLA*& slot = sa[idx]; |
|---|
| 757 | #ifdef DEBUG |
|---|
| 758 | std::cerr << "Chosen slot = " << (void*)slot << "\n"; |
|---|
| 759 | #endif |
|---|
| 760 | anchor_accessor aa; |
|---|
| 761 | aa(i)->anchor.set_next(0); |
|---|
| 762 | // XXX if is_occupied(slot) for others |
|---|
| 763 | if( slot ) |
|---|
| 764 | { |
|---|
| 765 | IT* next = slot; |
|---|
| 766 | while( aa(next)->anchor.get_next() ) |
|---|
| 767 | { |
|---|
| 768 | #ifdef DEBUG |
|---|
| 769 | std::cerr << "Accessing slot/item " << (void*)slot << " and next being "; |
|---|
| 770 | #endif |
|---|
| 771 | next = aa(next)->anchor.get_next(); |
|---|
| 772 | #ifdef DEBUG |
|---|
| 773 | std::cerr << (void*)next << "\n"; |
|---|
| 774 | #endif |
|---|
| 775 | } |
|---|
| 776 | assert( next != NULL ); |
|---|
| 777 | // next now contains the last item in the list |
|---|
| 778 | // XXX singly linked list collision resolution |
|---|
| 779 | aa(next)->anchor.set_next(i); |
|---|
| 780 | } |
|---|
| 781 | else // its free, just set it |
|---|
| 782 | { |
|---|
| 783 | slot = i; |
|---|
| 784 | } |
|---|
| 785 | return true; |
|---|
| 786 | } |
|---|
| 787 | |
|---|
| 788 | template< |
|---|
| 789 | class IT, |
|---|
| 790 | class SZ, |
|---|
| 791 | class SLA |
|---|
| 792 | > |
|---|
| 793 | static bool remove( IT* it, SZ idx, SLA** sa ) |
|---|
| 794 | { |
|---|
| 795 | SLA*& slot = sa[idx]; |
|---|
| 796 | anchor_accessor aa; |
|---|
| 797 | if( slot == it ) |
|---|
| 798 | { |
|---|
| 799 | // If it is directly sitting at the slot remove it there. |
|---|
| 800 | slot = aa(it)->anchor.get_next(); |
|---|
| 801 | #ifdef DEBUG |
|---|
| 802 | aa(it)->anchor.set_next( reinterpret_cast<IT*>(~0UL) ); |
|---|
| 803 | #endif |
|---|
| 804 | return true; |
|---|
| 805 | } |
|---|
| 806 | else if( slot ) |
|---|
| 807 | { |
|---|
| 808 | // It is not the first in the slot but there is something |
|---|
| 809 | IT* next = slot; |
|---|
| 810 | while( aa(next)->anchor.get_next() ) |
|---|
| 811 | { |
|---|
| 812 | IT* prev = next; |
|---|
| 813 | next = aa(next)->anchor.get_next(); |
|---|
| 814 | if( next == it ) |
|---|
| 815 | { |
|---|
| 816 | aa(prev)->anchor.set_next( aa(next)->anchor.get_next() ); |
|---|
| 817 | #ifdef DEBUG |
|---|
| 818 | aa(next)->anchor.set_next( reinterpret_cast<IT*>(~0UL) ); |
|---|
| 819 | #endif |
|---|
| 820 | return true; |
|---|
| 821 | } |
|---|
| 822 | } |
|---|
| 823 | |
|---|
| 824 | } |
|---|
| 825 | return false; |
|---|
| 826 | } |
|---|
| 827 | |
|---|
| 828 | }; |
|---|
| 829 | |
|---|
| 830 | template<class ANCHOR_ACCESSOR> |
|---|
| 831 | struct double_inserter_template |
|---|
| 832 | { |
|---|
| 833 | typedef ANCHOR_ACCESSOR anchor_accessor; |
|---|
| 834 | |
|---|
| 835 | template<class AA> |
|---|
| 836 | struct rebind |
|---|
| 837 | { |
|---|
| 838 | typedef double_inserter_template<AA> other; |
|---|
| 839 | }; |
|---|
| 840 | |
|---|
| 841 | template< |
|---|
| 842 | class IT, |
|---|
| 843 | class SZ, |
|---|
| 844 | class SLA |
|---|
| 845 | > |
|---|
| 846 | bool operator()( IT* i, SZ idx, SLA** sa ) |
|---|
| 847 | { |
|---|
| 848 | SLA*& slot = sa[idx]; |
|---|
| 849 | #ifdef DEBUG |
|---|
| 850 | std::cerr << "Chosen slot = " << (void*)slot << "\n"; |
|---|
| 851 | #endif |
|---|
| 852 | // XXX if is_occupied(slot) for others |
|---|
| 853 | if( slot ) |
|---|
| 854 | { |
|---|
| 855 | anchor_accessor aa; |
|---|
| 856 | IT* next = slot; |
|---|
| 857 | while( aa(next)->get_next() ) |
|---|
| 858 | { |
|---|
| 859 | #ifdef DEBUG |
|---|
| 860 | std::cerr << "Accessing slot/item " << (void*)slot << " and next being "; |
|---|
| 861 | #endif |
|---|
| 862 | next = aa(next)->get_next(); |
|---|
| 863 | #ifdef DEBUG |
|---|
| 864 | std::cerr << (void*)next << "\n"; |
|---|
| 865 | #endif |
|---|
| 866 | } |
|---|
| 867 | // next now contains the last item in the list |
|---|
| 868 | // XXX singly linked list collision resolution |
|---|
| 869 | aa(next)->set_next(i); |
|---|
| 870 | aa(i)->set_next(0); |
|---|
| 871 | // ++elem_size; |
|---|
| 872 | } |
|---|
| 873 | else // its free, just set it |
|---|
| 874 | { |
|---|
| 875 | slot = i; |
|---|
| 876 | // ++elem_size; |
|---|
| 877 | } |
|---|
| 878 | return true; |
|---|
| 879 | } |
|---|
| 880 | }; |
|---|
| 881 | |
|---|
| 882 | struct double_inserter |
|---|
| 883 | { |
|---|
| 884 | template<class AA> |
|---|
| 885 | struct rebind |
|---|
| 886 | { |
|---|
| 887 | typedef double_inserter_template<AA> other; |
|---|
| 888 | }; |
|---|
| 889 | }; |
|---|
| 890 | |
|---|
| 891 | struct single_inserter |
|---|
| 892 | { |
|---|
| 893 | template<class AA> |
|---|
| 894 | struct rebind |
|---|
| 895 | { |
|---|
| 896 | typedef single_inserter_template<AA> other; |
|---|
| 897 | }; |
|---|
| 898 | }; |
|---|
| 899 | |
|---|
| 900 | |
|---|
| 901 | struct resolution_single_linked_list |
|---|
| 902 | { |
|---|
| 903 | template<class T, class PTR_SELECTOR> |
|---|
| 904 | struct anchor_member_type |
|---|
| 905 | { |
|---|
| 906 | typedef anchor_single<T,PTR_SELECTOR> type; |
|---|
| 907 | }; |
|---|
| 908 | typedef single_inserter inserter; |
|---|
| 909 | }; |
|---|
| 910 | |
|---|
| 911 | |
|---|
| 912 | /** |
|---|
| 913 | * This is a metafunction that provides the proper type for an anchor, given |
|---|
| 914 | * certain parameters. |
|---|
| 915 | * @tparam COLLISION_RESOLUTION is the metafunction that provides the way |
|---|
| 916 | * collisions are resolved. Depending on that the anchor may contain pointers |
|---|
| 917 | * (e.g. for linked list resolves) or also simply nothing. |
|---|
| 918 | * @tparam PTR_SELECTOR is the selector metafunction that transforms types into |
|---|
| 919 | * pointers to that type. We use it everywhere so that we can use special |
|---|
| 920 | * pointer types (shared pointers, relative pointers etc.) |
|---|
| 921 | * @tparam store_occupancy tells us whether we should provide storage for |
|---|
| 922 | * whether this item is occupied or not. This makes only sense if you want to |
|---|
| 923 | * use an open addressing hashtable, as such the collision resolution should be |
|---|
| 924 | * chosen in an appropriate way. |
|---|
| 925 | * @tparam store_hash tells us whether the hash value is ever stored or not. If |
|---|
| 926 | * it is stored, then it will be in a member of type size_t. |
|---|
| 927 | */ |
|---|
| 928 | template< |
|---|
| 929 | class T, |
|---|
| 930 | class COLLISION_RESOLUTION = resolution_single_linked_list , |
|---|
| 931 | class PTR_SELECTOR = raw_pointer, |
|---|
| 932 | bool store_hash = false, |
|---|
| 933 | bool store_occupancy = false |
|---|
| 934 | > |
|---|
| 935 | struct anchor_member_chooser |
|---|
| 936 | { |
|---|
| 937 | typedef |
|---|
| 938 | anchor_member_mixer< |
|---|
| 939 | typename COLLISION_RESOLUTION::template anchor_member_type<T,PTR_SELECTOR>::type, |
|---|
| 940 | store_hash, |
|---|
| 941 | store_occupancy> |
|---|
| 942 | type; |
|---|
| 943 | }; |
|---|
| 944 | |
|---|
| 945 | // ############################################# # # # # # # # # # # # # # # # # # # # # # # # # |
|---|
| 946 | // |
|---|
| 947 | // We do not want the user to manually chose (however he of course can do so) |
|---|
| 948 | // the necessary anchor for his hash table, but we want to provide a |
|---|
| 949 | // metafunction that can do so. Therefore we create here something that |
|---|
| 950 | // collects all the properties for the future hashtable that will not depend on |
|---|
| 951 | // the type of data being stored. Within that embedded will be a metafunction |
|---|
| 952 | // that chooses the anchor type. |
|---|
| 953 | // Those marked with * influence the anchor type |
|---|
| 954 | // |
|---|
| 955 | // Not all of these things may be combined |
|---|
| 956 | //*- collision resolution scheme |
|---|
| 957 | // - single linked list |
|---|
| 958 | // - double linked list |
|---|
| 959 | // - jumping (various weird kinds) |
|---|
| 960 | // - rehashing (various ways, including one with alternative hashers) |
|---|
| 961 | // - cookoo hashing |
|---|
| 962 | // - allocator storage |
|---|
| 963 | // - whether to store the allocator passed to the ctor in a member variable or not |
|---|
| 964 | // - comparator storage |
|---|
| 965 | // - whether to store the comparator passed to the ctor in a member variable or not |
|---|
| 966 | //*- data storage |
|---|
| 967 | // - in the table (OA like), item will be copied (precludes linked list et al collision resolution) |
|---|
| 968 | // - as pointers, items will be "linked" into the datastructure |
|---|
| 969 | //*- pointer type |
|---|
| 970 | // - default is a raw pointer |
|---|
| 971 | // - metafunction that will lead to a pointer type when fed with T |
|---|
| 972 | //*- store hash |
|---|
| 973 | // - storing the hash (without the upper bit) set might be necessary in case |
|---|
| 974 | // the hashing is so expensive that its worth it |
|---|
| 975 | // - ownership |
|---|
| 976 | // - data structure owns data (on destruction, all items are destroyed) |
|---|
| 977 | // - the user owns the data. On destruction, items will not be touched at all |
|---|
| 978 | // - size and access |
|---|
| 979 | // - size is always multiple of 2, access to slots is done by masking of bits |
|---|
| 980 | // - size is an arbitrary number (likely prime). slots will be chosen by |
|---|
| 981 | // taking the remainder of division modulo the size |
|---|
| 982 | // - table storage |
|---|
| 983 | // - default is std::vector |
|---|
| 984 | // - a metafunction or template can be provided that will determine the type |
|---|
| 985 | // of the underlying table storage. |
|---|
| 986 | // - the size_type |
|---|
| 987 | // - type that is used to store various underlying values |
|---|
| 988 | // - automatic resize on insert/remove. |
|---|
| 989 | // - enable per template |
|---|
| 990 | // - disable per template |
|---|
| 991 | // - have a runtime changeable flag with templated default |
|---|
| 992 | // - duplicate entries |
|---|
| 993 | // - allow/disallow per template parameter |
|---|
| 994 | // - compare hash |
|---|
| 995 | // - if store hash is enabled, before comparing the elements themselves, the hash is compared |
|---|
| 996 | // - allocator and storage |
|---|
| 997 | // - an allocator must be specified which can be stored optionally, have a param that tells us |
|---|
| 998 | // - same for a comparator |
|---|
| 999 | // - also for the hasher we might want to store it (or not) |
|---|
| 1000 | // The result must be in the hash_config passed to the hash_table |
|---|
| 1001 | // ############################################# # # # # # # # # # # # # # # # # # # # # # # # # |
|---|
| 1002 | // For the following things, we need to have T available in some way. |
|---|
| 1003 | // - hashing |
|---|
| 1004 | // - user provides a functor or metafunction or whatever that can hash T into |
|---|
| 1005 | // an integer |
|---|
| 1006 | // - We provide means for hashing standard datastructures and arbitrary byte |
|---|
| 1007 | // buffers in three categories, among the user could chose for his datatypes too. |
|---|
| 1008 | // - identity. Do not hash things, useful for those cases where the key |
|---|
| 1009 | // is known to be distributed properly |
|---|
| 1010 | // - trivial is a very simple way to determine the hash |
|---|
| 1011 | // - fnv uses a bit more complex hashing, similar to fnv |
|---|
| 1012 | // - anchor access |
|---|
| 1013 | // - the user provides a mean for accessing the appropriate anchor for this |
|---|
| 1014 | // hashtable when having a T* at hand |
|---|
| 1015 | // - for OA-like hashtables that store copies in the table itself, the anchor |
|---|
| 1016 | // shall provide a way to mark items as occupied, free or deleted. Each |
|---|
| 1017 | // element *must* be default constructible and *must* be initially marked |
|---|
| 1018 | // as free. Failure to do so will lead to undefined behaviour |
|---|
| 1019 | // The result must be in the hash_accessor passed to the hash_table |
|---|
| 1020 | namespace collision_resolution |
|---|
| 1021 | { |
|---|
| 1022 | |
|---|
| 1023 | /** |
|---|
| 1024 | * This shall resolve collisions by putting things in a singly linked list. |
|---|
| 1025 | * We gonna need functionality for specifying the type of the anchor and a |
|---|
| 1026 | * function to resolve things in case of a collision. Lets start with simple |
|---|
| 1027 | * ones and adapt the interface for every of them. |
|---|
| 1028 | */ |
|---|
| 1029 | #if 0 |
|---|
| 1030 | template< |
|---|
| 1031 | class T, |
|---|
| 1032 | class POINTER_TYPE, |
|---|
| 1033 | class ANCHOR_ACCESSOR |
|---|
| 1034 | > |
|---|
| 1035 | struct resolve_single_linked |
|---|
| 1036 | { |
|---|
| 1037 | typedef T item_type; |
|---|
| 1038 | typedef POINTER_TYPE<item_type>::type pointer_type; |
|---|
| 1039 | typedef anchor_single<T,POINTER_TYPE> anchor_type; |
|---|
| 1040 | typedef ANCHOR_ACCESSOR<anchor_type> anchor_accessor; |
|---|
| 1041 | |
|---|
| 1042 | /** |
|---|
| 1043 | * @param colliding is the element at the slot where the collision has been detected. |
|---|
| 1044 | * @param added is the element that shall be added to the hashtable. After |
|---|
| 1045 | * returning, this element is a successful member of the hashtable. |
|---|
| 1046 | */ |
|---|
| 1047 | static void resolve( T* colliding, T* added ) |
|---|
| 1048 | { |
|---|
| 1049 | T* nx = colliding; |
|---|
| 1050 | T* tmp; |
|---|
| 1051 | while(tmp = anchor_accessor::get(nx).get_next()) |
|---|
| 1052 | { |
|---|
| 1053 | nx = tmp; |
|---|
| 1054 | } |
|---|
| 1055 | anchor_accessor::get(nx).set_next(added); |
|---|
| 1056 | anchor_accessor::get(added).set_next(0); |
|---|
| 1057 | } |
|---|
| 1058 | |
|---|
| 1059 | struct iterator |
|---|
| 1060 | { |
|---|
| 1061 | pointer_type* slot_position; |
|---|
| 1062 | pointer_type item; |
|---|
| 1063 | iterator& operator++(int); |
|---|
| 1064 | iterator& operator++(); |
|---|
| 1065 | }; |
|---|
| 1066 | }; |
|---|
| 1067 | #endif |
|---|
| 1068 | |
|---|
| 1069 | } |
|---|
| 1070 | |
|---|
| 1071 | #if 0 |
|---|
| 1072 | template< |
|---|
| 1073 | class T, |
|---|
| 1074 | template<class,class> class COLLISION_RESOLUTION, |
|---|
| 1075 | template<class,class> class DATA_ACCESS, |
|---|
| 1076 | template<class> class POINTER_TYPE |
|---|
| 1077 | > |
|---|
| 1078 | struct hash_intrusion_config // weird name but for OA style we don't have any anchor... |
|---|
| 1079 | { |
|---|
| 1080 | /** |
|---|
| 1081 | * Just define the type of the object that shall be stored |
|---|
| 1082 | */ |
|---|
| 1083 | typedef T item_type; |
|---|
| 1084 | |
|---|
| 1085 | /** |
|---|
| 1086 | * Metafunction that transforms any item type into the pointer type |
|---|
| 1087 | * We leave this as a metafunction and do not directly use a pointer type |
|---|
| 1088 | * since maybe at other places we need pointers too but have some different |
|---|
| 1089 | * item type. Just be flexible. |
|---|
| 1090 | */ |
|---|
| 1091 | template<class IT> |
|---|
| 1092 | struct pointer_type |
|---|
| 1093 | { |
|---|
| 1094 | typedef typename POINTER_TYPE<IT>::type type; |
|---|
| 1095 | }; |
|---|
| 1096 | |
|---|
| 1097 | /** |
|---|
| 1098 | * This should somehow resemble the slot type. It will depend on the actual |
|---|
| 1099 | * planned collision resolution and also on where to store what, usually |
|---|
| 1100 | * its though either the element itself or a pointer to it. |
|---|
| 1101 | */ |
|---|
| 1102 | template<class IT> |
|---|
| 1103 | struct slot_type |
|---|
| 1104 | { |
|---|
| 1105 | }; |
|---|
| 1106 | |
|---|
| 1107 | /* |
|---|
| 1108 | * anchor type depends on collision resolution. If its single/double linked |
|---|
| 1109 | * list then we have anchors, if its some re-hashing or OA we just have |
|---|
| 1110 | * nothing. Ideally the anchor would not eixst then giving us compile time |
|---|
| 1111 | * errors. |
|---|
| 1112 | */ |
|---|
| 1113 | typedef typename COLLISION_RESOLUTION<T, |
|---|
| 1114 | STORE_HASH, |
|---|
| 1115 | POINTER_TYPE |
|---|
| 1116 | >::anchor_type anchor_type; |
|---|
| 1117 | }; |
|---|
| 1118 | #endif |
|---|
| 1119 | |
|---|
| 1120 | template< |
|---|
| 1121 | class T0, |
|---|
| 1122 | class T1, |
|---|
| 1123 | class T2, |
|---|
| 1124 | class T3, |
|---|
| 1125 | class T4, |
|---|
| 1126 | class T5 |
|---|
| 1127 | > |
|---|
| 1128 | struct hash_config |
|---|
| 1129 | { |
|---|
| 1130 | }; |
|---|
| 1131 | |
|---|
| 1132 | // Here we wrap the storage type that the user wants us to use in a hashtable |
|---|
| 1133 | template<class T> |
|---|
| 1134 | struct storage_wrapper |
|---|
| 1135 | { |
|---|
| 1136 | }; |
|---|
| 1137 | |
|---|
| 1138 | template<class A> |
|---|
| 1139 | struct anchor_accessor_single |
|---|
| 1140 | { |
|---|
| 1141 | typedef typename A::item_type item_type; |
|---|
| 1142 | A* operator()( item_type* i ) |
|---|
| 1143 | { |
|---|
| 1144 | // XXX This all is total mockup, we really need a way to |
|---|
| 1145 | // flexibly accessing the proper member (possible via member |
|---|
| 1146 | // pointers). This interface must be suitable for the |
|---|
| 1147 | // generated item types from UDTs. |
|---|
| 1148 | return &(i->anchor); // XXX make this a refernece |
|---|
| 1149 | } |
|---|
| 1150 | }; |
|---|
| 1151 | |
|---|
| 1152 | #if 0 |
|---|
| 1153 | template<class T> |
|---|
| 1154 | struct hash_anchor_oa |
|---|
| 1155 | { |
|---|
| 1156 | /** |
|---|
| 1157 | * To mark this element as occupied/free/deleted |
|---|
| 1158 | */ |
|---|
| 1159 | unsigned char status; |
|---|
| 1160 | }; |
|---|
| 1161 | |
|---|
| 1162 | template<class T> |
|---|
| 1163 | struct hash_item_double |
|---|
| 1164 | { |
|---|
| 1165 | typedef T item_type; |
|---|
| 1166 | hash_anchor_double<T> anchor; |
|---|
| 1167 | T item; |
|---|
| 1168 | }; |
|---|
| 1169 | |
|---|
| 1170 | /** |
|---|
| 1171 | * There is no real anchor for OA based items. Either we have just a plain |
|---|
| 1172 | * pointer to them or they are already fully in the table. |
|---|
| 1173 | */ |
|---|
| 1174 | template<class T> |
|---|
| 1175 | struct hash_item_oa |
|---|
| 1176 | { |
|---|
| 1177 | typedef T item_type; |
|---|
| 1178 | }; |
|---|
| 1179 | |
|---|
| 1180 | template<class T> |
|---|
| 1181 | struct hash_item_single |
|---|
| 1182 | { |
|---|
| 1183 | typedef T item_type; |
|---|
| 1184 | hash_anchor_single<T> anchor; |
|---|
| 1185 | T item; |
|---|
| 1186 | }; |
|---|
| 1187 | |
|---|
| 1188 | /** |
|---|
| 1189 | * For open addressing based hashtables there is the possibility of using this |
|---|
| 1190 | * only as collision resolution, and just store the pointers in there, or to |
|---|
| 1191 | * store the whole object in there. This is the variant of the item thats just |
|---|
| 1192 | * for the pointers. |
|---|
| 1193 | */ |
|---|
| 1194 | template<class T> |
|---|
| 1195 | struct hash_item_oa_pointer |
|---|
| 1196 | { |
|---|
| 1197 | typedef T item_type; |
|---|
| 1198 | item_type* item; |
|---|
| 1199 | }; |
|---|
| 1200 | |
|---|
| 1201 | template<class T> |
|---|
| 1202 | struct hash_item_oa_item |
|---|
| 1203 | { |
|---|
| 1204 | typedef T item_type; |
|---|
| 1205 | item_type item; |
|---|
| 1206 | }; |
|---|
| 1207 | #endif |
|---|
| 1208 | /** |
|---|
| 1209 | * This class controls how the single items that are being stored in the table |
|---|
| 1210 | * are accessed. |
|---|
| 1211 | * If there is no special accessor that uses anchors embedded in the usertype, |
|---|
| 1212 | * then this will generate nodes containing the users type in them and all |
|---|
| 1213 | * inserts will be copies instead of "hanging" the node into the table. |
|---|
| 1214 | */ |
|---|
| 1215 | template<class T> |
|---|
| 1216 | struct hash_accessor |
|---|
| 1217 | { |
|---|
| 1218 | typedef typename T::item_type item_type; |
|---|
| 1219 | |
|---|
| 1220 | }; |
|---|
| 1221 | |
|---|
| 1222 | template<bool store_per_instance, class H> |
|---|
| 1223 | struct hasher_storage_base; |
|---|
| 1224 | |
|---|
| 1225 | template<class H> |
|---|
| 1226 | struct hasher_storage_base<true,H> |
|---|
| 1227 | { |
|---|
| 1228 | H hasher_instance; |
|---|
| 1229 | }; |
|---|
| 1230 | |
|---|
| 1231 | template<class H> |
|---|
| 1232 | struct hasher_storage_base<false,H> |
|---|
| 1233 | { |
|---|
| 1234 | static H hasher_instance; |
|---|
| 1235 | }; |
|---|
| 1236 | |
|---|
| 1237 | template<class H> |
|---|
| 1238 | H hasher_storage_base<false,H>::hasher_instance; |
|---|
| 1239 | |
|---|
| 1240 | template<bool store_per_instance, class H> |
|---|
| 1241 | struct allocator_storage_base; |
|---|
| 1242 | |
|---|
| 1243 | template<class H> |
|---|
| 1244 | struct allocator_storage_base<true,H> |
|---|
| 1245 | { |
|---|
| 1246 | H allocator_instance; |
|---|
| 1247 | }; |
|---|
| 1248 | |
|---|
| 1249 | template<class H> |
|---|
| 1250 | struct allocator_storage_base<false,H> |
|---|
| 1251 | { |
|---|
| 1252 | static H allocator_instance; |
|---|
| 1253 | }; |
|---|
| 1254 | |
|---|
| 1255 | template<class H> |
|---|
| 1256 | H allocator_storage_base<false,H>::allocator_instance; |
|---|
| 1257 | |
|---|
| 1258 | template< |
|---|
| 1259 | class T, |
|---|
| 1260 | class ATYPE, |
|---|
| 1261 | ATYPE T::* AMEMBER |
|---|
| 1262 | > |
|---|
| 1263 | struct anchor_member_accessor |
|---|
| 1264 | { |
|---|
| 1265 | ATYPE* operator()( T& t ) { return &( t.*AMEMBER); } |
|---|
| 1266 | ATYPE* operator()( T* t ) { return &( t->*AMEMBER); } |
|---|
| 1267 | }; |
|---|
| 1268 | |
|---|
| 1269 | template<class T> |
|---|
| 1270 | struct item_compare |
|---|
| 1271 | { |
|---|
| 1272 | bool operator()( const T& l, const T& r ) const |
|---|
| 1273 | { |
|---|
| 1274 | return l == r; |
|---|
| 1275 | } |
|---|
| 1276 | }; |
|---|
| 1277 | |
|---|
| 1278 | template< class T , class K , K T::* M > |
|---|
| 1279 | struct member_compare |
|---|
| 1280 | { |
|---|
| 1281 | template<class R> |
|---|
| 1282 | bool operator()( const T& l, const R& r ) const |
|---|
| 1283 | { |
|---|
| 1284 | return l.*M == r; |
|---|
| 1285 | } |
|---|
| 1286 | |
|---|
| 1287 | bool operator()( const T& l, const T& r ) const |
|---|
| 1288 | { |
|---|
| 1289 | return l.*M == r.*M; |
|---|
| 1290 | } |
|---|
| 1291 | |
|---|
| 1292 | }; |
|---|
| 1293 | struct auto_size_always |
|---|
| 1294 | { |
|---|
| 1295 | bool auto_size( ) const { return true; } |
|---|
| 1296 | }; |
|---|
| 1297 | |
|---|
| 1298 | struct auto_size_never |
|---|
| 1299 | { |
|---|
| 1300 | bool auto_size( ) const { return false; } |
|---|
| 1301 | }; |
|---|
| 1302 | |
|---|
| 1303 | struct empty_base_owns |
|---|
| 1304 | { |
|---|
| 1305 | }; |
|---|
| 1306 | |
|---|
| 1307 | struct empty_base_size |
|---|
| 1308 | { |
|---|
| 1309 | }; |
|---|
| 1310 | |
|---|
| 1311 | |
|---|
| 1312 | enum class storage_levels |
|---|
| 1313 | { |
|---|
| 1314 | storage_level_auto_size, |
|---|
| 1315 | storage_level_owns_items |
|---|
| 1316 | }; |
|---|
| 1317 | |
|---|
| 1318 | struct auto_size_bool_storage |
|---|
| 1319 | { |
|---|
| 1320 | bool a_size; |
|---|
| 1321 | |
|---|
| 1322 | template<int> |
|---|
| 1323 | void store( bool b ) |
|---|
| 1324 | { |
|---|
| 1325 | a_size = b; |
|---|
| 1326 | } |
|---|
| 1327 | |
|---|
| 1328 | template<int> |
|---|
| 1329 | bool load( void ) const |
|---|
| 1330 | { |
|---|
| 1331 | return a_size; |
|---|
| 1332 | } |
|---|
| 1333 | }; |
|---|
| 1334 | |
|---|
| 1335 | struct owns_items_bool_storage |
|---|
| 1336 | { |
|---|
| 1337 | bool o_items; |
|---|
| 1338 | |
|---|
| 1339 | template<int> |
|---|
| 1340 | void store( bool b ) |
|---|
| 1341 | { |
|---|
| 1342 | o_items = b; |
|---|
| 1343 | } |
|---|
| 1344 | |
|---|
| 1345 | template<int> |
|---|
| 1346 | bool load( void ) const |
|---|
| 1347 | { |
|---|
| 1348 | return o_items; |
|---|
| 1349 | } |
|---|
| 1350 | }; |
|---|
| 1351 | /** |
|---|
| 1352 | * important: This must always go at least once through rebind to make the |
|---|
| 1353 | * mpl::if_c call so the use_bitset_storage parameter is reflected correctly. |
|---|
| 1354 | */ |
|---|
| 1355 | template<bool def = true, bool use_bitset_storage = true, class T = void > |
|---|
| 1356 | struct auto_size_runtime |
|---|
| 1357 | : public boost::mpl::if_c<use_bitset_storage,empty_base_size,auto_size_bool_storage>::type |
|---|
| 1358 | { |
|---|
| 1359 | auto_size_runtime( ) |
|---|
| 1360 | { |
|---|
| 1361 | set_auto_size(def); |
|---|
| 1362 | } |
|---|
| 1363 | |
|---|
| 1364 | static const storage_levels storage_level = storage_levels::storage_level_auto_size; |
|---|
| 1365 | |
|---|
| 1366 | static const bool needs_bitset_storage = use_bitset_storage; |
|---|
| 1367 | |
|---|
| 1368 | void set_auto_size( bool en = true) |
|---|
| 1369 | { |
|---|
| 1370 | static_cast<T*>(this)->template store<storage_level>(en); |
|---|
| 1371 | } |
|---|
| 1372 | |
|---|
| 1373 | bool auto_size( ) const |
|---|
| 1374 | { |
|---|
| 1375 | return static_cast<const T*>(this)->template load<storage_level>(); |
|---|
| 1376 | } |
|---|
| 1377 | |
|---|
| 1378 | template<class U> |
|---|
| 1379 | struct rebind |
|---|
| 1380 | { |
|---|
| 1381 | typedef auto_size_runtime<def,use_bitset_storage, |
|---|
| 1382 | typename boost::mpl::if_c<use_bitset_storage, U, auto_size_bool_storage>::type > other; |
|---|
| 1383 | }; |
|---|
| 1384 | }; |
|---|
| 1385 | |
|---|
| 1386 | template<bool def = true, bool use_bitset_storage = true, class T = void > |
|---|
| 1387 | struct owns_items_runtime |
|---|
| 1388 | : public boost::mpl::if_c<use_bitset_storage,empty_base_owns,owns_items_bool_storage>::type |
|---|
| 1389 | { |
|---|
| 1390 | owns_items_runtime( ) |
|---|
| 1391 | { |
|---|
| 1392 | set_owns_items(def); |
|---|
| 1393 | } |
|---|
| 1394 | |
|---|
| 1395 | static const storage_levels storage_level = storage_levels::storage_level_owns_items; |
|---|
| 1396 | |
|---|
| 1397 | static const bool needs_bitset_storage = use_bitset_storage; |
|---|
| 1398 | |
|---|
| 1399 | void set_owns_items( bool en = true) |
|---|
| 1400 | { |
|---|
| 1401 | static_cast<T*>(this)->template store<storage_level>(en); |
|---|
| 1402 | } |
|---|
| 1403 | |
|---|
| 1404 | bool owns_items( ) const |
|---|
| 1405 | { |
|---|
| 1406 | return static_cast<const T*>(this)->template load<storage_level>(); |
|---|
| 1407 | } |
|---|
| 1408 | |
|---|
| 1409 | template<class U> |
|---|
| 1410 | struct rebind |
|---|
| 1411 | { |
|---|
| 1412 | typedef owns_items_runtime<def,use_bitset_storage, |
|---|
| 1413 | typename boost::mpl::if_c<use_bitset_storage, U, owns_items_bool_storage>::type > other; |
|---|
| 1414 | }; |
|---|
| 1415 | }; |
|---|
| 1416 | |
|---|
| 1417 | |
|---|
| 1418 | |
|---|
| 1419 | template<class T0, class T1> |
|---|
| 1420 | struct policy_bool_storage : |
|---|
| 1421 | T0::template rebind<policy_bool_storage<T0,T1> >::other, |
|---|
| 1422 | T1::template rebind<policy_bool_storage<T0,T1> >::other |
|---|
| 1423 | { |
|---|
| 1424 | bool b0 : 1; |
|---|
| 1425 | bool b1 : 1; |
|---|
| 1426 | |
|---|
| 1427 | template<unsigned N> |
|---|
| 1428 | void store( bool b ) |
|---|
| 1429 | { |
|---|
| 1430 | switch(N) |
|---|
| 1431 | { |
|---|
| 1432 | case 0: |
|---|
| 1433 | b0 = b; |
|---|
| 1434 | break; |
|---|
| 1435 | case 1: |
|---|
| 1436 | b1 = b; |
|---|
| 1437 | break; |
|---|
| 1438 | // intentionally no default case, anything else here is UB |
|---|
| 1439 | } |
|---|
| 1440 | } |
|---|
| 1441 | |
|---|
| 1442 | template<unsigned N> |
|---|
| 1443 | bool load( void ) const |
|---|
| 1444 | { |
|---|
| 1445 | switch(N) |
|---|
| 1446 | { |
|---|
| 1447 | case 0: |
|---|
| 1448 | return b0; |
|---|
| 1449 | case 1: |
|---|
| 1450 | return b1; |
|---|
| 1451 | } |
|---|
| 1452 | } |
|---|
| 1453 | }; |
|---|
| 1454 | |
|---|
| 1455 | struct three_bytes_test |
|---|
| 1456 | { |
|---|
| 1457 | char c0; |
|---|
| 1458 | char c1; |
|---|
| 1459 | char c2; |
|---|
| 1460 | }; |
|---|
| 1461 | #if 0 |
|---|
| 1462 | // planned final class interface something like: |
|---|
| 1463 | hash_table<T,ACCESSOR,HASHER, |
|---|
| 1464 | store_hasher<true>, |
|---|
| 1465 | equal_comparator<COMPARE>, |
|---|
| 1466 | size_type<int>, |
|---|
| 1467 | auto_size<auto_size_runtime<false> >, |
|---|
| 1468 | |
|---|
| 1469 | #endif |
|---|
| 1470 | |
|---|
| 1471 | template<bool STORE_MASK, class SIZE_TYPE, class SLOT_RESIZER> |
|---|
| 1472 | struct mask_storage_base; |
|---|
| 1473 | |
|---|
| 1474 | template<class SIZE_TYPE, class SLOT_RESIZER> |
|---|
| 1475 | struct mask_storage_base<true,SIZE_TYPE,SLOT_RESIZER> |
|---|
| 1476 | { |
|---|
| 1477 | typedef SLOT_RESIZER slot_resizer; |
|---|
| 1478 | typedef typename slot_resizer::slot_selector hash_slot_selector; |
|---|
| 1479 | typedef SIZE_TYPE size_type; |
|---|
| 1480 | |
|---|
| 1481 | size_type h_mask; |
|---|
| 1482 | |
|---|
| 1483 | void reset_mask( size_t len ) |
|---|
| 1484 | { |
|---|
| 1485 | h_mask = hash_slot_selector::mask_from_length(len); |
|---|
| 1486 | } |
|---|
| 1487 | |
|---|
| 1488 | size_type hash_mask( size_t /*len*/ ) const |
|---|
| 1489 | { |
|---|
| 1490 | return h_mask; |
|---|
| 1491 | } |
|---|
| 1492 | }; |
|---|
| 1493 | |
|---|
| 1494 | template<class SIZE_TYPE, class SLOT_RESIZER> |
|---|
| 1495 | struct mask_storage_base<false,SIZE_TYPE,SLOT_RESIZER> |
|---|
| 1496 | { |
|---|
| 1497 | typedef SLOT_RESIZER slot_resizer; |
|---|
| 1498 | typedef typename slot_resizer::slot_selector hash_slot_selector; |
|---|
| 1499 | typedef SIZE_TYPE size_type; |
|---|
| 1500 | |
|---|
| 1501 | void reset_mask( size_t /*len*/ ) |
|---|
| 1502 | { |
|---|
| 1503 | } |
|---|
| 1504 | |
|---|
| 1505 | size_type hash_mask( size_t len ) const |
|---|
| 1506 | { |
|---|
| 1507 | return hash_slot_selector::mask_from_length(len); |
|---|
| 1508 | } |
|---|
| 1509 | }; |
|---|
| 1510 | |
|---|
| 1511 | /** |
|---|
| 1512 | */ |
|---|
| 1513 | template<class T |
|---|
| 1514 | , class SIZE_TYPE |
|---|
| 1515 | , class HASHER |
|---|
| 1516 | , bool STORE_HASHER |
|---|
| 1517 | , bool STORE_MASK |
|---|
| 1518 | , class SLOT_RESIZER |
|---|
| 1519 | , class ANCHOR_ACCESSOR |
|---|
| 1520 | , class COMPARE |
|---|
| 1521 | , class INSERTER |
|---|
| 1522 | , class AUTOSIZE |
|---|
| 1523 | , class OWNAGE |
|---|
| 1524 | > |
|---|
| 1525 | class hash_table : |
|---|
| 1526 | protected hasher_storage_base<STORE_HASHER,HASHER>, |
|---|
| 1527 | protected mask_storage_base<STORE_MASK,SIZE_TYPE,SLOT_RESIZER>, |
|---|
| 1528 | // protected allocator_storage_base<STORE_ALLOCATOR,ALLOCATOR> |
|---|
| 1529 | // XXX MPL check for whether we do not better derive from empty base |
|---|
| 1530 | public policy_bool_storage<AUTOSIZE, OWNAGE> |
|---|
| 1531 | // XXX later check how many size_type members we have at most and at least. |
|---|
| 1532 | // If this differs much, and they are also shattered around then it could |
|---|
| 1533 | // make sense to do something like the policy_bool_storage for size_type so |
|---|
| 1534 | // that alignment issues due to derivation do not play any role. |
|---|
| 1535 | { |
|---|
| 1536 | public: |
|---|
| 1537 | typedef policy_bool_storage<AUTOSIZE, OWNAGE> pbstorage; |
|---|
| 1538 | /** |
|---|
| 1539 | * This is the selector type that will give us many mayn informations about |
|---|
| 1540 | * other types. |
|---|
| 1541 | */ |
|---|
| 1542 | // typedef S hash_selector; |
|---|
| 1543 | |
|---|
| 1544 | /** |
|---|
| 1545 | * This is the type of the items or "nodes" that the user will put into our |
|---|
| 1546 | * hashtable. Depending on the selector, our array will contain those items |
|---|
| 1547 | * explicitly, or pointers to them, that then will either resemble singly |
|---|
| 1548 | * linked or doubly linked lists. |
|---|
| 1549 | */ |
|---|
| 1550 | typedef T item_type; |
|---|
| 1551 | |
|---|
| 1552 | typedef SIZE_TYPE size_type; |
|---|
| 1553 | typedef item_type* slot_item_type; |
|---|
| 1554 | // typedef std::vector<slot_item_type> table_type; |
|---|
| 1555 | typedef slot_item_type* table_type; |
|---|
| 1556 | |
|---|
| 1557 | typedef SLOT_RESIZER slot_resizer; |
|---|
| 1558 | typedef typename slot_resizer::slot_selector hash_slot_selector; |
|---|
| 1559 | typedef ANCHOR_ACCESSOR anchor_accessor; |
|---|
| 1560 | |
|---|
| 1561 | typedef COMPARE compare; |
|---|
| 1562 | |
|---|
| 1563 | // typedef INSERTER inserter; |
|---|
| 1564 | typedef typename INSERTER::template rebind<ANCHOR_ACCESSOR>::other inserter; |
|---|
| 1565 | |
|---|
| 1566 | |
|---|
| 1567 | // typedef typename hash_selector::size_type size_type; |
|---|
| 1568 | // typedef K key_type; |
|---|
| 1569 | // typedef typename hash_selector::template slot_item<item_type>::type slot_item_type; |
|---|
| 1570 | // typedef typename hash_selector::template add_pointer<slot_item_type>::type table_type; |
|---|
| 1571 | |
|---|
| 1572 | /** |
|---|
| 1573 | * We are a nullary metafunction returning ourselves. Who knows what this |
|---|
| 1574 | * is good for... |
|---|
| 1575 | */ |
|---|
| 1576 | typedef hash_table type; |
|---|
| 1577 | // typedef V<slot_item_type> sequence_type; |
|---|
| 1578 | // typedef IT iterator; |
|---|
| 1579 | // typedef IT const_iterator; |
|---|
| 1580 | /* template<class T,...> |
|---|
| 1581 | struct |
|---|
| 1582 | { |
|---|
| 1583 | typedef hash_table<T,...> type; |
|---|
| 1584 | } helper_type; |
|---|
| 1585 | */ |
|---|
| 1586 | private: |
|---|
| 1587 | table_type data; |
|---|
| 1588 | |
|---|
| 1589 | /** |
|---|
| 1590 | * Amount of elements actually contained within the hashtable. |
|---|
| 1591 | */ |
|---|
| 1592 | size_type elem_size; |
|---|
| 1593 | |
|---|
| 1594 | /** |
|---|
| 1595 | * The length of the slots array/vector. |
|---|
| 1596 | */ |
|---|
| 1597 | size_type h_length; |
|---|
| 1598 | |
|---|
| 1599 | protected: |
|---|
| 1600 | public: |
|---|
| 1601 | hash_table( size_type i_s ) : |
|---|
| 1602 | elem_size(0) |
|---|
| 1603 | { |
|---|
| 1604 | h_length = slot_resizer::optimal_size(i_s); |
|---|
| 1605 | data = new slot_item_type[h_length]; |
|---|
| 1606 | // XXX This fill is redundant if the slot item type is a UDT. It makes |
|---|
| 1607 | // only sense if it is a pointer type. |
|---|
| 1608 | std::fill_n( data, h_length, slot_item_type()); |
|---|
| 1609 | this->reset_mask(table_size()); |
|---|
| 1610 | #ifdef DEBUG |
|---|
| 1611 | std::cerr << "i_s = " << i_s << ", h_length " << h_length << ", h_mask = " << this->hash_mask(table_size()) << "\n"; |
|---|
| 1612 | #endif |
|---|
| 1613 | } |
|---|
| 1614 | |
|---|
| 1615 | /* currently broken, recreate as soon as the above ctor is stable and we |
|---|
| 1616 | * can provide useful defaults here. |
|---|
| 1617 | * XXX Make sure empty hashtables work well with no dynamic storage |
|---|
| 1618 | * allocated. (especially iterators etc. will have to work properly) |
|---|
| 1619 | hash_table( ) : |
|---|
| 1620 | data(0), |
|---|
| 1621 | elem_size(0), |
|---|
| 1622 | h_mask(0) |
|---|
| 1623 | { |
|---|
| 1624 | } |
|---|
| 1625 | */ |
|---|
| 1626 | |
|---|
| 1627 | ~hash_table( ) |
|---|
| 1628 | { |
|---|
| 1629 | // XXX allocation and deallocation should be routed through a policy so |
|---|
| 1630 | // we can use policies that allocate in e.g. memory mapped files using |
|---|
| 1631 | // relative pointers. |
|---|
| 1632 | clear(); |
|---|
| 1633 | delete[] data; |
|---|
| 1634 | } |
|---|
| 1635 | |
|---|
| 1636 | hash_table( const hash_table& ) = delete; |
|---|
| 1637 | hash_table& operator=( const hash_table& ) = delete; |
|---|
| 1638 | |
|---|
| 1639 | // insert variant that allows duplicates and uses single linked lists for |
|---|
| 1640 | // collision resolution. |
|---|
| 1641 | /** |
|---|
| 1642 | * @returns true if the insert was successfully, the item should be removed |
|---|
| 1643 | * from the structure prior to deletion, as removing will likely need |
|---|
| 1644 | * access to the item. If false is returned, this means that the item has |
|---|
| 1645 | * not been inserted. In this case the user is always responsible for |
|---|
| 1646 | * handling this item, e.g. delete it. |
|---|
| 1647 | */ |
|---|
| 1648 | // IWEAR_WARN_UNUSED_RESULT |
|---|
| 1649 | bool insert( item_type* i ) |
|---|
| 1650 | { |
|---|
| 1651 | // XXX note: a shrink on insert doesn't really make sense. If the table |
|---|
| 1652 | // is bigger then it is because someone has removed something without |
|---|
| 1653 | // having the table shrink, or its being constructed to hold many |
|---|
| 1654 | // elements. |
|---|
| 1655 | if( maybe_grow() ) |
|---|
| 1656 | { |
|---|
| 1657 | #ifdef DEBUG |
|---|
| 1658 | std::cerr << "Hashtable changed its size\n"; |
|---|
| 1659 | #endif |
|---|
| 1660 | } |
|---|
| 1661 | return insert_direct( i ); |
|---|
| 1662 | } |
|---|
| 1663 | |
|---|
| 1664 | /*private*/ |
|---|
| 1665 | bool insert_direct( item_type* i ) |
|---|
| 1666 | { |
|---|
| 1667 | #ifdef DEBUG |
|---|
| 1668 | // std::cerr << "sizeof(pbstorage) " << sizeof(pbstorage) << "\n"; |
|---|
| 1669 | #endif |
|---|
| 1670 | #ifdef DEBUG |
|---|
| 1671 | std::cerr << "Inserting " << i << "\n"; |
|---|
| 1672 | #endif |
|---|
| 1673 | // generate hash |
|---|
| 1674 | size_type hvalue = hasher_instance(i); |
|---|
| 1675 | #ifdef DEBUG |
|---|
| 1676 | std::cerr << "hvalue = " << hvalue << "\n"; |
|---|
| 1677 | #endif |
|---|
| 1678 | // generate slot position |
|---|
| 1679 | size_type idx = hash_slot_selector()(hvalue,this->hash_mask(table_size())); |
|---|
| 1680 | #ifdef DEBUG |
|---|
| 1681 | std::cerr << "idx = " << idx << ", data is " << (void*)data << "\n"; |
|---|
| 1682 | #endif |
|---|
| 1683 | |
|---|
| 1684 | // get the slot at hands... |
|---|
| 1685 | // although the table_type might be some relative pointer thingie, we |
|---|
| 1686 | // use a plain pointer here since this is for internal purposes only |
|---|
| 1687 | // and any compatible pointer should have conversions to/from us |
|---|
| 1688 | |
|---|
| 1689 | bool ret = inserter::insert(i,idx,&data[0]); |
|---|
| 1690 | if( ret ) |
|---|
| 1691 | { |
|---|
| 1692 | elem_size++; |
|---|
| 1693 | } |
|---|
| 1694 | // XXX check whether duplicates are allowed |
|---|
| 1695 | // do collision prevention |
|---|
| 1696 | // set slot and/or do list linking, if not already done by |
|---|
| 1697 | // collision prevention |
|---|
| 1698 | return false; |
|---|
| 1699 | } |
|---|
| 1700 | |
|---|
| 1701 | void resize_slots( size_t nslots ) |
|---|
| 1702 | { |
|---|
| 1703 | #ifdef DEBUG |
|---|
| 1704 | std::cerr << "resize_slots(" << nslots << ")\n"; |
|---|
| 1705 | #endif |
|---|
| 1706 | assert( nslots >= size() ); |
|---|
| 1707 | // XXX Invent some smart pointer like things to make this all exception |
|---|
| 1708 | // safe. |
|---|
| 1709 | |
|---|
| 1710 | // Save a bunch of useful old values |
|---|
| 1711 | table_type odata = data; |
|---|
| 1712 | size_type olen = h_length; |
|---|
| 1713 | size_type omask = this->hash_mask(h_length); |
|---|
| 1714 | #ifdef DEBUG |
|---|
| 1715 | size_type oelem = elem_size; |
|---|
| 1716 | #endif |
|---|
| 1717 | |
|---|
| 1718 | // now set the new wanted values |
|---|
| 1719 | h_length = nslots; |
|---|
| 1720 | data = new slot_item_type[h_length]; |
|---|
| 1721 | std::fill_n( data, h_length, slot_item_type()); |
|---|
| 1722 | this->reset_mask(h_length); |
|---|
| 1723 | |
|---|
| 1724 | // Since for inserting we pretend to be an empty hashtable currently we |
|---|
| 1725 | // set our size to 0 |
|---|
| 1726 | elem_size = 0; |
|---|
| 1727 | |
|---|
| 1728 | #ifdef DEBUG |
|---|
| 1729 | #ifdef DEBUG |
|---|
| 1730 | std::cerr << "re-inserting " << oelem << " elements \n"; |
|---|
| 1731 | #endif |
|---|
| 1732 | #endif |
|---|
| 1733 | for( size_t i = 0; i < olen; ++i ) |
|---|
| 1734 | { |
|---|
| 1735 | slot_item_type it = odata[i]; |
|---|
| 1736 | while(it) |
|---|
| 1737 | { |
|---|
| 1738 | #ifdef DEBUG |
|---|
| 1739 | std::cerr << "re-inserting item chain starting at " << (void*)it << "\n"; |
|---|
| 1740 | #endif |
|---|
| 1741 | // do an erase() on the old data (needs to be kept in sync with |
|---|
| 1742 | // erase() |
|---|
| 1743 | size_type hvalue = hasher_instance(it); |
|---|
| 1744 | size_type idx = hash_slot_selector()(hvalue,omask); |
|---|
| 1745 | inserter::remove(it,idx,&odata[0]); |
|---|
| 1746 | |
|---|
| 1747 | // this will insert into the new storage, not checking and/or |
|---|
| 1748 | // triggering any resizes. |
|---|
| 1749 | insert_direct(it); |
|---|
| 1750 | it = odata[i]; |
|---|
| 1751 | } |
|---|
| 1752 | } |
|---|
| 1753 | #ifdef DEBUG |
|---|
| 1754 | assert( oelem == elem_size ); |
|---|
| 1755 | #endif |
|---|
| 1756 | // data has been transferred, dispose of the slots storage |
|---|
| 1757 | delete[] odata; |
|---|
| 1758 | } |
|---|
| 1759 | |
|---|
| 1760 | // XXX when we have a "never resize" or "never shrink" policy, make this |
|---|
| 1761 | // function not remove the internal storage table. Otherwise pretend that |
|---|
| 1762 | // "shrink to fit" was called. |
|---|
| 1763 | /** |
|---|
| 1764 | * @returns true if a resize of the underlying table storage has been done. |
|---|
| 1765 | */ |
|---|
| 1766 | bool clear ( ) |
|---|
| 1767 | { |
|---|
| 1768 | #ifdef DEBUG |
|---|
| 1769 | std::cerr << "Clearing hashtable of " << size() << " elements \n"; |
|---|
| 1770 | #endif |
|---|
| 1771 | for( size_t i = 0; i < h_length; ++i ) |
|---|
| 1772 | { |
|---|
| 1773 | slot_item_type it = data[i]; |
|---|
| 1774 | while(it) |
|---|
| 1775 | { |
|---|
| 1776 | // do an erase() on the old data (needs to be kept in sync with |
|---|
| 1777 | // erase() |
|---|
| 1778 | size_type hvalue = hasher_instance(it); |
|---|
| 1779 | size_type idx = hash_slot_selector()(hvalue,this->hash_mask(this->table_size())); |
|---|
| 1780 | inserter::remove(it,idx,&data[0]); |
|---|
| 1781 | if( this->owns_items() ) |
|---|
| 1782 | { |
|---|
| 1783 | delete it; |
|---|
| 1784 | } |
|---|
| 1785 | it = data[i]; |
|---|
| 1786 | } |
|---|
| 1787 | } |
|---|
| 1788 | return shrink_to_fit(); |
|---|
| 1789 | } |
|---|
| 1790 | |
|---|
| 1791 | /** |
|---|
| 1792 | * @returns true if a table resize has been done |
|---|
| 1793 | */ |
|---|
| 1794 | bool shrink_to_fit( ) |
|---|
| 1795 | { |
|---|
| 1796 | // XXX unimplemented |
|---|
| 1797 | return false; |
|---|
| 1798 | } |
|---|
| 1799 | |
|---|
| 1800 | bool empty( ) const |
|---|
| 1801 | { |
|---|
| 1802 | return elem_size == 0; |
|---|
| 1803 | } |
|---|
| 1804 | |
|---|
| 1805 | size_type max_size( ) const |
|---|
| 1806 | { |
|---|
| 1807 | return slot_resizer::max_size(); |
|---|
| 1808 | } |
|---|
| 1809 | /** |
|---|
| 1810 | * @param num is the number of elements to prepare the hashtable for being |
|---|
| 1811 | * stored. If this is bigger than what the current size of the hashtable is |
|---|
| 1812 | * optimal for, then a resize will be triggered. |
|---|
| 1813 | * @returns true if the size of the hashtable was actually changed. |
|---|
| 1814 | */ |
|---|
| 1815 | bool reserve( size_t num ) |
|---|
| 1816 | { |
|---|
| 1817 | size_type sz = slot_resizer::optimal_size(num); |
|---|
| 1818 | if( sz > table_size() ) |
|---|
| 1819 | { |
|---|
| 1820 | resize_slots( sz ); |
|---|
| 1821 | return true; |
|---|
| 1822 | } |
|---|
| 1823 | return false; |
|---|
| 1824 | } |
|---|
| 1825 | |
|---|
| 1826 | /** |
|---|
| 1827 | * Checks if the table needs to be resized according to our |
|---|
| 1828 | * resize policy and returns true if it was done, false |
|---|
| 1829 | * otherwise. |
|---|
| 1830 | */ |
|---|
| 1831 | /*private*/ |
|---|
| 1832 | // __attribute__((noinline)) |
|---|
| 1833 | bool maybe_grow( void ) |
|---|
| 1834 | { |
|---|
| 1835 | // XXX split auto resize policy into "auto grow" and "auto shrink". |
|---|
| 1836 | // Some people might never want the table to shrink automatically but |
|---|
| 1837 | // to grow. We will then also have to make the actual calculations for |
|---|
| 1838 | // grow/shrink depending on those values as they are now are done |
|---|
| 1839 | // together. |
|---|
| 1840 | |
|---|
| 1841 | // check if we do auto size at all |
|---|
| 1842 | if( this->auto_size() ) |
|---|
| 1843 | { |
|---|
| 1844 | #ifdef DEBUG |
|---|
| 1845 | std::cerr << "auto_resize check\n"; |
|---|
| 1846 | #endif |
|---|
| 1847 | if( __builtin_expect(slot_resizer::need_grow( size(), table_size() ),0) ) |
|---|
| 1848 | { |
|---|
| 1849 | #ifdef DEBUG |
|---|
| 1850 | std::cerr << "auto resize triggered\n"; |
|---|
| 1851 | #endif |
|---|
| 1852 | resize_slots( slot_resizer::optimal_size(size()) ); |
|---|
| 1853 | } |
|---|
| 1854 | } |
|---|
| 1855 | return false; |
|---|
| 1856 | } |
|---|
| 1857 | |
|---|
| 1858 | bool maybe_shrink( void ) |
|---|
| 1859 | { |
|---|
| 1860 | // check if we do auto size at all |
|---|
| 1861 | if( this->auto_size() ) |
|---|
| 1862 | { |
|---|
| 1863 | #ifdef DEBUG |
|---|
| 1864 | std::cerr << "auto_resize check\n"; |
|---|
| 1865 | #endif |
|---|
| 1866 | if( slot_resizer::need_shrink( size(), table_size() ) ) |
|---|
| 1867 | { |
|---|
| 1868 | #ifdef DEBUG |
|---|
| 1869 | std::cerr << "auto resize triggered\n"; |
|---|
| 1870 | #endif |
|---|
| 1871 | resize_slots( slot_resizer::optimal_size(size()) ); |
|---|
| 1872 | } |
|---|
| 1873 | } |
|---|
| 1874 | return false; |
|---|
| 1875 | } |
|---|
| 1876 | /* |
|---|
| 1877 | iterator erase( iterator i ) |
|---|
| 1878 | { |
|---|
| 1879 | iterator tmp(i); |
|---|
| 1880 | ++tmp; |
|---|
| 1881 | erase(&*i); |
|---|
| 1882 | return tmp; |
|---|
| 1883 | } |
|---|
| 1884 | */ |
|---|
| 1885 | |
|---|
| 1886 | bool erase( item_type* it ) |
|---|
| 1887 | { |
|---|
| 1888 | // XXX Make sure that for elements caching their hash values we reset |
|---|
| 1889 | // the hash value to "unstored" |
|---|
| 1890 | size_type hvalue = hasher_instance(it); |
|---|
| 1891 | size_type idx = hash_slot_selector()(hvalue,this->hash_mask(table_size())); |
|---|
| 1892 | bool ret = inserter::remove(it,idx,&data[0]); |
|---|
| 1893 | |
|---|
| 1894 | if( ret ) |
|---|
| 1895 | { |
|---|
| 1896 | maybe_shrink(); |
|---|
| 1897 | } |
|---|
| 1898 | |
|---|
| 1899 | return ret; |
|---|
| 1900 | } |
|---|
| 1901 | |
|---|
| 1902 | /** |
|---|
| 1903 | * |
|---|
| 1904 | */ |
|---|
| 1905 | item_type* find( ) { return const_cast<item_type*>(const_cast<const type*>(this)->find()); } |
|---|
| 1906 | |
|---|
| 1907 | /** |
|---|
| 1908 | * Find something. |
|---|
| 1909 | * @param k is the key to search in the hashtable for. There must exist an |
|---|
| 1910 | * operator() in the hash template parameter that takes such a type, and |
|---|
| 1911 | * also this must be comparable via operator== to the item_type. |
|---|
| 1912 | * XXX Think about whether it makes sense to return an iterator instead. |
|---|
| 1913 | * Most of the cases we don't want anything besides the item, and for |
|---|
| 1914 | * iterating any position is as good as the one from find, since after all |
|---|
| 1915 | * we cannot expect to know anything about where things went. |
|---|
| 1916 | */ |
|---|
| 1917 | template<class K> |
|---|
| 1918 | const item_type* find( const K& k ) const |
|---|
| 1919 | { |
|---|
| 1920 | // XXX Try to figure out if we do the exact same sequence of operations |
|---|
| 1921 | // for find/insert too, and if yes lets have a look if we can put it |
|---|
| 1922 | // into one single function, so that we only have one place to maintain |
|---|
| 1923 | // it. |
|---|
| 1924 | size_type hvalue = hasher_instance(k); |
|---|
| 1925 | #ifdef DEBUG |
|---|
| 1926 | std::cerr << "Trying to find a K \"" << k << "\" with hash " << hvalue << "\n"; |
|---|
| 1927 | #endif |
|---|
| 1928 | |
|---|
| 1929 | size_type idx = hash_slot_selector()(hvalue,this->hash_mask(table_size())); |
|---|
| 1930 | #ifdef DEBUG |
|---|
| 1931 | std::cerr << "First trying at index idx = " << idx << ", data is " << (void*)data << "\n"; |
|---|
| 1932 | #endif |
|---|
| 1933 | |
|---|
| 1934 | slot_item_type* slot = data + idx; |
|---|
| 1935 | #ifdef DEBUG |
|---|
| 1936 | std::cerr << "Chosen slot = " << (void*)slot << "\n"; |
|---|
| 1937 | #endif |
|---|
| 1938 | if( *slot ) |
|---|
| 1939 | { |
|---|
| 1940 | anchor_accessor aa; |
|---|
| 1941 | item_type* next = *slot; |
|---|
| 1942 | while( next && !compare()(*next,k) ) |
|---|
| 1943 | { |
|---|
| 1944 | next = aa(next)->anchor.get_next(); |
|---|
| 1945 | } |
|---|
| 1946 | #ifdef DEBUG |
|---|
| 1947 | std::cerr << "Returning element at " << (void*)next << "\n"; |
|---|
| 1948 | #endif |
|---|
| 1949 | return next; |
|---|
| 1950 | } |
|---|
| 1951 | else |
|---|
| 1952 | { |
|---|
| 1953 | #ifdef DEBUG |
|---|
| 1954 | std::cerr << "Chosen slot is empty\n"; |
|---|
| 1955 | #endif |
|---|
| 1956 | return 0; |
|---|
| 1957 | } |
|---|
| 1958 | } |
|---|
| 1959 | |
|---|
| 1960 | |
|---|
| 1961 | /** |
|---|
| 1962 | * Returns the amount of occupied elements. |
|---|
| 1963 | */ |
|---|
| 1964 | size_type size() const { return elem_size; } |
|---|
| 1965 | |
|---|
| 1966 | size_type table_size() const |
|---|
| 1967 | { |
|---|
| 1968 | return h_length; |
|---|
| 1969 | } |
|---|
| 1970 | }; |
|---|
| 1971 | |
|---|
| 1972 | // below come some convenience wrapper templates that behave more or less like the stdlib alternatives. |
|---|
| 1973 | /* |
|---|
| 1974 | template<class T, class HT<T> = hash_table > |
|---|
| 1975 | hash_set |
|---|
| 1976 | { |
|---|
| 1977 | public: |
|---|
| 1978 | typedef size_t size_type; |
|---|
| 1979 | typedef T item_type; |
|---|
| 1980 | typedef typename HT::accesor_type accessor_type; |
|---|
| 1981 | private: |
|---|
| 1982 | protected: |
|---|
| 1983 | public: |
|---|
| 1984 | bool insert( item_type& ); |
|---|
| 1985 | bool insert( item_type* ); |
|---|
| 1986 | bool erase( item_type& ); |
|---|
| 1987 | bool erase( item_type* ); |
|---|
| 1988 | iterator find( item_type* ); |
|---|
| 1989 | iterator find( item_type& ); |
|---|
| 1990 | iterator find( key_type& ); |
|---|
| 1991 | iterator find( size_type ); |
|---|
| 1992 | void reserve( size_type ); |
|---|
| 1993 | size_type capacity( void ) const; |
|---|
| 1994 | bool resize( size_type ); |
|---|
| 1995 | |
|---|
| 1996 | const_iterator begin( void ) const; |
|---|
| 1997 | iterator begin( void ); |
|---|
| 1998 | const_iterator end( void ) const; |
|---|
| 1999 | iterator end( void ); |
|---|
| 2000 | |
|---|
| 2001 | }; |
|---|
| 2002 | |
|---|
| 2003 | template<class T, class HT<...> = hash_table > |
|---|
| 2004 | hash_map |
|---|
| 2005 | { |
|---|
| 2006 | }; |
|---|
| 2007 | */ |
|---|
| 2008 | } // namespace iwear |
|---|
| 2009 | |
|---|
| 2010 | |
|---|
| 2011 | /* |
|---|
| 2012 | * Future improvements |
|---|
| 2013 | * - make the load ratios optionally configurable at runtime. |
|---|
| 2014 | */ |
|---|
| 2015 | // vim: tabstop=4 shiftwidth=4 noexpandtab |
|---|