1 /*
2  * Copyright (C) 2010-2011 Dmitry Marakasov
3  *
4  * This file is part of glosm.
5  *
6  * glosm is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * glosm is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with glosm.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 
20 #ifndef ID_MAP_HH
21 #define ID_MAP_HH
22 
23 #include <vector>
24 
25 /**
26  * Custom std::map-like container for storing OSM data effeciently.
27  *
28  * Uses lower bits of object id as a hash for no calculation overhead
29  * and pooled data storage to pack elements effeciently.
30  *
31  * Interface and usage semantics are the same as for std::map
32  */
33 template <typename I, typename T, int BUCKET_COUNT_ORDER = 0, int REHASH_GROWTH_ORDER = 1, int ITEMS_PER_CHUNK = 16*65536>
34 class id_map {
35 	static const size_t chunk_size = ITEMS_PER_CHUNK;
36 
37 public:
38 	typedef I                                key_type;
39 	typedef T                                mapped_type;
40 	typedef std::pair<const I, T>            value_type;
41 
42 	typedef value_type*                      pointer;
43 	typedef const value_type*                const_pointer;
44 	typedef value_type&                      reference;
45 	typedef const value_type&                const_reference;
46 
47 private:
48 	struct hash_node {
49 		value_type data;
50 		hash_node* next;
51 	};
52 
53 	typedef hash_node*                       hash_node_ptr;
54 	typedef const hash_node*                 const_hash_node_ptr;
55 
56 public:
57 	struct iterator {
58 		typedef iterator _self;
59 		typedef id_map<I, T, BUCKET_COUNT_ORDER, REHASH_GROWTH_ORDER, ITEMS_PER_CHUNK> _map;
60 
61 		typedef const _map*                       const_map_ptr;
62 
63 		typedef typename _map::pointer            pointer;
64 		typedef typename _map::reference          reference;
65 		typedef typename _map::hash_node_ptr      hash_node_ptr;
66 
67 		const_map_ptr map;
68 		hash_node_ptr current;
69 
iteratorid_map::iterator70 		iterator() : map(), current() {}
iteratorid_map::iterator71 		iterator(const_map_ptr m) : map(m), current() {}
iteratorid_map::iterator72 		iterator(const_map_ptr m, hash_node_ptr c) : map(m), current(c) {}
73 
operator ++id_map::iterator74 		_self& operator++() {
75 			if (current->next)
76 				current = current->next;
77 			else
78 				current = map->findfirstafter(current->data.first);
79 			return *this;
80 		}
81 
operator ++id_map::iterator82 		_self operator++(int) {
83 			_self tmp = *this;
84 			if (current->next)
85 				current = current->next;
86 			else
87 				current = map->findfirstafter(current->data.first);
88 			return tmp;
89 		}
90 
operator *id_map::iterator91 		reference operator*() const {
92 			return current->data;
93 		}
94 
operator ->id_map::iterator95 		pointer operator->() const {
96 			return &current->data;
97 		}
98 
operator ==id_map::iterator99 		bool operator==(const _self& x) const {
100 			return x.current == current;
101 		}
102 
operator !=id_map::iterator103 		bool operator!=(const _self& x) const {
104 			return x.current != current;
105 		}
106 	};
107 
108 	struct const_iterator {
109 		typedef const_iterator _self;
110 		typedef id_map<I, T, BUCKET_COUNT_ORDER, REHASH_GROWTH_ORDER, ITEMS_PER_CHUNK> _map;
111 
112 		typedef const _map*                        const_map_ptr;
113 
114 		typedef typename _map::iterator            iterator;
115 
116 		typedef typename _map::const_pointer       pointer;
117 		typedef typename _map::const_reference     reference;
118 		typedef typename _map::const_hash_node_ptr const_hash_node_ptr;
119 
120 		const_map_ptr map;
121 		const_hash_node_ptr current;
122 
const_iteratorid_map::const_iterator123 		const_iterator() : map(), current() {}
const_iteratorid_map::const_iterator124 		const_iterator(const_map_ptr m) : map(m) {}
const_iteratorid_map::const_iterator125 		const_iterator(const_map_ptr m, const_hash_node_ptr c) : map(m), current(c) {}
const_iteratorid_map::const_iterator126 		const_iterator(const iterator& it): map(it.map), current(it.current) {}
127 
operator ++id_map::const_iterator128 		_self& operator++() {
129 			if (current->next)
130 				current = current->next;
131 			else
132 				current = map->findfirstafter(current->data.first);
133 			return *this;
134 		}
135 
operator ++id_map::const_iterator136 		_self operator++(int) {
137 			_self tmp = *this;
138 			if (current->next)
139 				current = current->next;
140 			else
141 				current = map->findfirstafter(current->data.first);
142 			return tmp;
143 		}
144 
operator *id_map::const_iterator145 		reference operator*() const {
146 			return current->data;
147 		}
148 
operator ->id_map::const_iterator149 		pointer operator->() const {
150 			return &current->data;
151 		}
152 
operator ==id_map::const_iterator153 		bool operator==(const _self& x) const {
154 			return x.current == current;
155 		}
156 
operator !=id_map::const_iterator157 		bool operator!=(const _self& x) const {
158 			return x.current != current;
159 		}
160 	};
161 
162 public:
163 	typedef std::vector<hash_node*> chunk_list;
164 
165 public:
id_map()166 	id_map() {
167 		init();
168 	}
169 
~id_map()170 	virtual ~id_map() {
171 		deinit();
172 	}
173 
insert(const value_type & v)174 	std::pair<iterator, bool> insert(const value_type& v) {
175 		if (REHASH_GROWTH_ORDER > 0 && count > nbuckets * 2)
176 			rehash(REHASH_GROWTH_ORDER);
177 
178 		hash_node* t = reinterpret_cast<hash_node*>(alloc());
179 		new((void*)&t->data)value_type(v);
180 
181 		/* No checking for existing element done - we assume OSM
182 		 * data to not have objects with duplicate id's */
183 		t->next = buckets[v.first & (nbuckets-1)];
184 		buckets[v.first & (nbuckets-1)] = t;
185 		count++;
186 
187 		return std::make_pair(iterator(this, t), true);
188 	}
189 
size() const190 	size_t size() const {
191 		return count;
192 	}
193 
clear()194 	void clear() {
195 		deinit();
196 		init();
197 	}
198 
find(key_type v)199 	iterator find(key_type v) {
200 		for (hash_node* n = buckets[v & (nbuckets-1)]; n; n = n->next)
201 			if (n->data.first == v)
202 				return iterator(this, n);
203 
204 		return end();
205 	}
206 
find(key_type v) const207 	const_iterator find(key_type v) const {
208 		for (const hash_node* n = buckets[v & (nbuckets-1)]; n; n = n->next)
209 			if (n->data.first == v)
210 				return const_iterator(this, n);
211 
212 		return end();
213 	}
214 
begin()215 	iterator begin() {
216 		if (count == 0)
217 			return end();
218 
219 		return iterator(this, findfirst());
220 	}
221 
begin() const222 	const_iterator begin() const {
223 		if (count == 0)
224 			return end();
225 
226 		return const_iterator(this, findfirst());
227 	}
228 
end()229 	iterator end() {
230 		return iterator(this, NULL);
231 	}
232 
end() const233 	const_iterator end() const {
234 		return const_iterator(this, NULL);
235 	}
236 
237 protected:
findfirst() const238 	hash_node* findfirst() const {
239 		for (hash_node** b = buckets; b < buckets + nbuckets; ++b)
240 			if (*b != NULL)
241 				return *b;
242 
243 		return NULL;
244 	}
245 
findfirstfor(key_type v) const246 	hash_node* findfirstfor(key_type v) const {
247 		for (hash_node** b = buckets + (v & (nbuckets-1)); b < buckets + nbuckets; ++b)
248 			if (*b != NULL)
249 				return *b;
250 
251 		return NULL;
252 	}
253 
findfirstafter(key_type v) const254 	hash_node* findfirstafter(key_type v) const {
255 		for (hash_node** b = buckets + (v & (nbuckets-1)) + 1; b < buckets + nbuckets; ++b)
256 			if (*b != NULL)
257 				return *b;
258 
259 		return NULL;
260 	}
261 
alloc()262 	hash_node* alloc() {
263 		if (last_chunk_free == 0) {
264 			/* chunk filled; allocate new one */
265 			chunks.push_back(reinterpret_cast<hash_node*>(::operator new(chunk_size*sizeof(hash_node))));
266 			current_ptr = chunks.back();
267 			last_chunk_free = chunk_size;
268 		}
269 		hash_node* ret = current_ptr;
270 		current_ptr++;
271 		last_chunk_free--;
272 		return ret;
273 	}
274 
rehash(int k)275 	void rehash(int k) {
276 		int newnbuckets = nbuckets * (1 << k);
277 
278 		hash_node** newbuckets = new hash_node*[newnbuckets];
279 		memset(newbuckets, 0, newnbuckets * sizeof(hash_node*));
280 
281 		for (hash_node** b = buckets; b < buckets + nbuckets; ++b) {
282 			for (hash_node* n = *b; n != NULL; ) {
283 				hash_node* cur = n;
284 				n = n->next;
285 				cur->next = newbuckets[cur->data.first & (newnbuckets-1)];
286 				newbuckets[cur->data.first & (newnbuckets-1)] = cur;
287 			}
288 		}
289 
290 		nbuckets = newnbuckets;
291 		delete[] buckets;
292 		buckets = newbuckets;
293 	}
294 
deinit()295 	void deinit() {
296 		for (typename chunk_list::iterator c = chunks.begin(); c != chunks.end(); ++c) {
297 			/* Call destructors for assigned elements */
298 			for (hash_node* i = *c; i < ((*c == chunks.back()) ? (*c + chunk_size - last_chunk_free) : (*c + chunk_size)); ++i) {
299 				i->data.~value_type();
300 			}
301 			::operator delete(*c);
302 		}
303 
304 		chunks.clear();
305 
306 		delete[] buckets;
307 	}
308 
init()309 	void init() {
310 		count = 0;
311 		last_chunk_free = 0;
312 		nbuckets = 1 << BUCKET_COUNT_ORDER;
313 		buckets = new hash_node*[nbuckets];
314 		memset(buckets, 0, nbuckets * sizeof(hash_node*));
315 	}
316 
317 protected:
318 	/* hash table */
319 	size_t nbuckets; /* always power of 2, so nbuckets-1 is bitmask for hashing */
320 	hash_node** buckets;
321 	size_t count; /* @todo superfluous - may be derived from nchunks and urrent_ptr */
322 
323 	/* memory pool */
324 	chunk_list chunks;
325 	size_t last_chunk_free;
326 	hash_node* current_ptr; /* @todo superfluous - either last_chunk free or current_ptr should be removed */
327 };
328 
329 #endif
330