1 /** 2 Copyright: Copyright (c) 2015-2017 Andrey Penechko. 3 License: $(WEB boost.org/LICENSE_1_0.txt, Boost License 1.0). 4 Authors: Andrey Penechko. 5 */ 6 module voxelman.container.intkeyhashset; 7 8 import voxelman.math : nextPOT; 9 10 struct IntKeyHashSet(Key, Key nullKey = Key.max) 11 { 12 import std.experimental.allocator.gc_allocator; 13 import std.experimental.allocator.mallocator; 14 15 Key[] keys; 16 size_t length; 17 18 private bool resizing; 19 20 //alias allocator = Mallocator.instance; 21 alias allocator = GCAllocator.instance; 22 23 this(ubyte[] array, size_t length) { 24 keys = cast(Key[])array; 25 this.length = length; 26 } 27 28 ubyte[] getTable() { 29 return cast(ubyte[])keys; 30 } 31 32 @property size_t capacity() const { return keys.length; } 33 @property bool empty() const { return length == 0; } 34 35 bool remove(Key key) { 36 auto idx = findIndex(key); 37 if (idx == size_t.max) return false; 38 auto i = idx; 39 while (true) 40 { 41 keys[i] = nullKey; 42 43 size_t j = i, r; 44 do { 45 if (++i >= keys.length) i -= keys.length; 46 if (keys[i] == nullKey) 47 { 48 --length; 49 return true; 50 } 51 r = keys[i] & (keys.length-1); 52 } 53 while ((j<r && r<=i) || (i<j && j<r) || (r<=i && i<j)); 54 keys[j] = keys[i]; 55 } 56 } 57 58 void clear() { 59 keys[] = nullKey; 60 length = 0; 61 } 62 63 void put(Key key) { 64 grow(1); 65 auto i = findInsertIndex(key); 66 if (keys[i] != key) ++length; 67 68 keys[i] = key; 69 } 70 71 bool opIndex(Key key) inout { 72 auto idx = findIndex(key); 73 return idx != size_t.max; 74 } 75 76 bool opBinaryRight(string op)(Key key) inout if (op == "in") { 77 auto idx = findIndex(key); 78 return idx != size_t.max; 79 } 80 81 int opApply(scope int delegate(Key) del) { 82 foreach (i; 0 .. keys.length) 83 if (keys[i] != nullKey) 84 if (auto ret = del(keys[i])) 85 return ret; 86 return 0; 87 } 88 89 void reserve(size_t amount) { 90 auto newcap = ((length + amount) * 3) / 2; 91 resize(newcap); 92 } 93 94 void shrink() { 95 auto newcap = length * 3 / 2; 96 resize(newcap); 97 } 98 99 private size_t findIndex(Key key) const { 100 if (length == 0) return size_t.max; 101 size_t start = key & (keys.length-1); 102 auto i = start; 103 while (keys[i] != key) { 104 if (keys[i] == nullKey) return size_t.max; 105 if (++i >= keys.length) i -= keys.length; 106 if (i == start) return size_t.max; 107 } 108 return i; 109 } 110 111 private size_t findInsertIndex(Key key) const { 112 size_t target = key & (keys.length-1); 113 auto i = target; 114 while (keys[i] != nullKey && keys[i] != key) { 115 if (++i >= keys.length) i -= keys.length; 116 assert (i != target, "No free bucket found, HashMap full!?"); 117 } 118 return i; 119 } 120 121 private void grow(size_t amount) { 122 auto newsize = length + amount; 123 if (newsize < (keys.length*2)/3) return; 124 auto newcap = keys.length ? keys.length : 1; 125 while (newsize >= (newcap*2)/3) newcap *= 2; 126 resize(newcap); 127 } 128 129 private void resize(size_t newSize) 130 { 131 assert(!resizing); 132 resizing = true; 133 scope(exit) resizing = false; 134 135 newSize = nextPOT(newSize); 136 137 auto oldKeys = keys; 138 139 if (newSize) { 140 void[] array = allocator.allocate(Key.sizeof * newSize); 141 keys = cast(Key[])array; 142 keys[] = nullKey; 143 foreach (i, ref key; oldKeys) { 144 if (key != nullKey) { 145 auto idx = findInsertIndex(key); 146 keys[idx] = key; 147 } 148 } 149 } else { 150 keys = null; 151 } 152 153 if (oldKeys) { 154 void[] arr = cast(void[])oldKeys; 155 allocator.deallocate(arr); 156 } 157 } 158 159 void toString()(scope void delegate(const(char)[]) sink) 160 { 161 import std.format : formattedWrite; 162 sink.formattedWrite("[",); 163 foreach(key; this) 164 { 165 sink.formattedWrite("%s, ", key); 166 } 167 sink.formattedWrite("]"); 168 } 169 }