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 }