1 /// Based on https://github.com/attractivechaos/klib commit 8ffbbdb2bf80ee84ba089dbabd5fa2420ee9aefe dated 2019-03-24 2 module intervaltree.khash; 3 4 import std.traits; // isIntegral 5 6 /* 7 #include <memory> 8 #include <functional> 9 #include <cstdlib> // for malloc() etc 10 #include <cstring> // for memset() 11 */ 12 import core.stdc.stdlib; // for malloc() etc 13 14 import core.std.stdint; // for uint32_t 15 16 //FIXME: doesn't work for 64-bit integers 17 //#define kroundup32(x) (--(x), (x)|=(x)>>1, (x)|=(x)>>2, (x)|=(x)>>4, (x)|=(x)>>8, (x)|=(x)>>16, ++(x)) 18 import intervaltree.roundup; 19 20 pragma(inline, true) { 21 ///#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) 22 auto __ac_isempty(T)(uint32_t* flag, T i) if (isIntegral!T) 23 { return ((flag[i>>4]>>((i&0xfU)<<1))&2); } 24 ///#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) 25 auto __ac_isdel(T)(uint32_t* flag, T i) if (isIntegral!T) 26 { return ((flag[i>>4]>>((i&0xfU)<<1))&1); } 27 ///#define __ac_isempty(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&2) 28 auto __ac_isempty(T)(uint32_t* flag, T i) if (isIntegral!T) 29 { return ((flag[i>>4]>>((i&0xfU)<<1))&2); } 30 ///#define __ac_isdel(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&1) 31 auto __ac_isdel(T)(uint32_t* flag, T i) if (isIntegral!T) 32 { return ((flag[i>>4]>>((i&0xfU)<<1))&1); } 33 ///#define __ac_iseither(flag, i) ((flag[i>>4]>>((i&0xfU)<<1))&3) 34 auto __ac_iseither(T)(uint32_t* flag, T i) if (isIntegral!T) 35 { return ((flag[i>>4]>>((i&0xfU)<<1))&3); } 36 ///#define __ac_set_isdel_false(flag, i) (flag[i>>4]&=~(1ul<<((i&0xfU)<<1))) 37 void __ac_set_isdel_false(T)(uint32_t* flag, T i) if (isIntegral!T) 38 { (flag[i>>4]&=~(1uL<<((i&0xfU)<<1))); } 39 ///#define __ac_set_isempty_false(flag, i) (flag[i>>4]&=~(2ul<<((i&0xfU)<<1))) 40 void __ac_set_isempty_false(T)(uint32_t* flag, T i) if (isIntegral!T) 41 { (flag[i>>4]&=~(2uL<<((i&0xfU)<<1))); } 42 ///#define __ac_set_isboth_false(flag, i) (flag[i>>4]&=~(3ul<<((i&0xfU)<<1))) 43 void __ac_set_isboth_false(T)(uint32_t* flag, T i) if (isIntegral!T) 44 { (flag[i>>4]&=~(3uL<<((i&0xfU)<<1))); } 45 ///#define __ac_set_isdel_true(flag, i) (flag[i>>4]|=1ul<<((i&0xfU)<<1)) 46 void __ac_set_isdel_true(T)(uint32_t* flag, T i) if (isIntegral!T) 47 { (flag[i>>4]|=1uL<<((i&0xfU)<<1)); } 48 } // pragma inline 49 50 /// If < 16 return 1, othewise return m / 16 51 /// #define __ac_fsize(m) ((m) < 16? 1 : (m)>>4) 52 string __ac_fsize(string m) { return "(" ~ m ~ "< 16? 1 : " ~ m ~ ">>4)"; } 53 54 ///template<class T, class Hash, class Eq = std::equal_to<T>, typename khint_t = uint32_t> 55 class KHash(T, hashfn, khint_t = uint32_t) 56 { 57 private: 58 khint_t n_buckets, count, n_occupied, upper_bound; 59 uint32_t *flags; 60 T *keys; 61 public: 62 ~this() 63 { 64 free(flags); 65 free(keys); 66 } 67 /// capacity (number of buckets) 68 khint_t capacity(void) const { return n_buckets; } 69 /// actual size (num objects) 70 khint_t size(void) const { return count; } 71 /// zero 72 khint_t begin(void) const { return 0; } 73 /// same as capacity / n.buckets 74 khint_t end(void) const { return n_buckets; } 75 76 /// does object exist at this integer offset? 77 void exist(khint_t x) const { return !__ac_iseither(flags, x); } 78 79 /// return bucket contents at integer offset by reference 80 ref T at(khint_t x) { return keys[x]; } // validate 81 82 /// get integer offset of value for key 83 khint_t get(ref const T key) const { 84 if (n_buckets) { 85 khint_t k, i, last, mask, step = 0; 86 mask = n_buckets - 1; 87 k = Hash()(key); i = k & mask; 88 last = i; 89 while (!__ac_isempty(flags, i) && (__ac_isdel(flags, i) || !Eq()(keys[i], key))) { 90 i = (i + (++step)) & mask; 91 if (i == last) return n_buckets; 92 } 93 return __ac_iseither(flags, i)? n_buckets : i; 94 } else return 0; 95 } 96 97 /// resize the hash table 98 int resize(khint_t new_n_buckets) { 99 uint32_t *new_flags = null; 100 khint_t j = 1; 101 { 102 new_n_buckets = kroundup32(new_n_buckets); // JSB: prior macro updates in place, my asm returns rounded value 103 if (new_n_buckets < 4) new_n_buckets = 4; 104 if (count >= (new_n_buckets>>1) + (new_n_buckets>>2)) j = 0; /* requested count is too small */ 105 else { /* hash table count to be changed (shrink or expand); rehash */ 106 new_flags = cast(uint32_t*) malloc( mixin(__ac_fsize(new_n_buckets)) * sizeof(uint32_t)); 107 if (!new_flags) return -1; 108 memset(new_flags, 0xaa, mixin(__ac_fsize(new_n_buckets)) * sizeof(uint32_t)); 109 if (n_buckets < new_n_buckets) { /* expand */ 110 T *new_keys = (T*)std::realloc((void *)keys, new_n_buckets * sizeof(T)); 111 if (!new_keys) { std::free(new_flags); return -1; } 112 keys = new_keys; 113 } /* otherwise shrink */ 114 } 115 } 116 if (j) { /* rehashing is needed */ 117 for (j = 0; j != n_buckets; ++j) { 118 if (__ac_iseither(flags, j) == 0) { 119 T key = keys[j]; 120 khint_t new_mask; 121 new_mask = new_n_buckets - 1; 122 __ac_set_isdel_true(flags, j); 123 while (1) { /* kick-out process; sort of like in Cuckoo hashing */ 124 khint_t k, i, step = 0; 125 k = Hash()(key); 126 i = k & new_mask; 127 while (!__ac_isempty(new_flags, i)) i = (i + (++step)) & new_mask; 128 __ac_set_isempty_false(new_flags, i); 129 if (i < n_buckets && __ac_iseither(flags, i) == 0) { /* kick out the existing element */ 130 { T tmp = keys[i]; keys[i] = key; key = tmp; } 131 __ac_set_isdel_true(flags, i); /* mark it as deleted in the old hash table */ 132 } else { /* write the element and jump out of the loop */ 133 keys[i] = key; 134 break; 135 } 136 } 137 } 138 } 139 if (n_buckets > new_n_buckets) /* shrink the hash table */ 140 keys = (T*)std::realloc((void *)keys, new_n_buckets * sizeof(T)); 141 free(flags); /* free the working space */ 142 flags = new_flags; 143 n_buckets = new_n_buckets; 144 n_occupied = count; 145 upper_bound = (n_buckets>>1) + (n_buckets>>2); 146 } 147 return 0; 148 } 149 150 /// put a new object in the hash table 151 khint_t put(const T &key, int *ret) { 152 khint_t x; 153 if (n_occupied >= upper_bound) { /* update the hash table */ 154 if (n_buckets > (count<<1)) { 155 if (resize(n_buckets - 1) < 0) { /* clear "deleted" elements */ 156 *ret = -1; return n_buckets; 157 } 158 } else if (resize(n_buckets + 1) < 0) { /* expand the hash table */ 159 *ret = -1; return n_buckets; 160 } 161 } /* TODO: to implement automatically shrinking; resize() already support shrinking */ 162 { 163 khint_t k, i, site, last, mask = n_buckets - 1, step = 0; 164 x = site = n_buckets; k = Hash()(key); i = k & mask; 165 if (__ac_isempty(flags, i)) x = i; /* for speed up */ 166 else { 167 last = i; 168 while (!__ac_isempty(flags, i) && (__ac_isdel(flags, i) || !Eq()(keys[i], key))) { 169 if (__ac_isdel(flags, i)) site = i; 170 i = (i + (++step)) & mask; 171 if (i == last) { x = site; break; } 172 } 173 if (x == n_buckets) { 174 if (__ac_isempty(flags, i) && site != n_buckets) x = site; 175 else x = i; 176 } 177 } 178 } 179 if (__ac_isempty(flags, x)) { /* not present at all */ 180 keys[x] = key; 181 __ac_set_isboth_false(flags, x); 182 ++count; ++n_occupied; 183 *ret = 1; 184 } else if (__ac_isdel(flags, x)) { /* deleted */ 185 keys[x] = key; 186 __ac_set_isboth_false(flags, x); 187 ++count; 188 *ret = 2; 189 } else *ret = 0; /* Don't touch keys[x] if present and not deleted */ 190 return x; 191 } 192 193 /// remove the object at integer offset 194 void del(khint_t x) { 195 if (x != n_buckets && !__ac_iseither(flags, x)) { 196 __ac_set_isdel_true(flags, x); 197 --count; 198 } 199 }//ok 200 }