Line data Source code
1 : /* src/toolbox/hashtable.hpp - functions for internal hashtables
2 :
3 : Copyright (C) 1996-2013
4 : CACAOVM - Verein zur Foerderung der freien virtuellen Maschine CACAO
5 :
6 : This file is part of CACAO.
7 :
8 : This program is free software; you can redistribute it and/or
9 : modify it under the terms of the GNU General Public License as
10 : published by the Free Software Foundation; either version 2, or (at
11 : your option) any later version.
12 :
13 : This program is distributed in the hope that it will be useful, but
14 : WITHOUT ANY WARRANTY; without even the implied warranty of
15 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 : General Public License for more details.
17 :
18 : You should have received a copy of the GNU General Public License
19 : along with this program; if not, write to the Free Software
20 : Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21 : 02110-1301, USA.
22 :
23 : */
24 :
25 :
26 : #ifndef HASHTABLE_HPP_
27 : #define HASHTABLE_HPP_ 1
28 :
29 : #include "config.h"
30 :
31 : #include <cassert> // for assert
32 : #include <cstddef> // for size_t
33 : #include <new> // for operator new
34 :
35 : #include "mm/memory.hpp" // for MemoryAllocator
36 :
37 : #include "toolbox/util.hpp" // for is_power_of_two, etc
38 :
39 : #include "vm/types.hpp" // for u4
40 : #include "vm/utf8.hpp" // for Utf8String
41 :
42 : class Mutex;
43 :
44 :
45 : /***
46 : * An instrusive hash table using open addressing.
47 : *
48 : * Entries stored in the table must conform to the following interface:
49 : * @code
50 : * struct Entry {
51 : * Entry();
52 : * Entry(const Entry&);
53 : *
54 : * // An Entry constructed with it's default constructor
55 : * // must return true for is_empty().
56 : * bool is_empty() const;
57 : * bool is_occupied() const;
58 : *
59 : * // make this an inline function that always
60 : * // returns false for an insert only table
61 : * bool is_deleted() const;
62 : *
63 : * void set_empty();
64 : * void set_occupied(const T&);
65 : *
66 : * // only required if you want to remove elements from the table,
67 : * // an insert-only table will work without it.
68 : * void set_deleted();
69 : *
70 : * // there must be an overload for any T that you want to insert
71 : * // into the table
72 : * void set_occupied(const T&);
73 : *
74 : * // these are only ever called on occupied entries
75 : * bool operator==(const T&);
76 : * size_t hash() const;
77 : * };
78 : * @endcode
79 : *
80 : * An entry can be in one of three states: empty, occupied and deleted.
81 : *
82 : * Each table entry must have an unique key that identifies it.
83 : * The key must be stored in the entry itself, it is not stored separately in
84 : * the table.
85 : *
86 : * After inserting an element its key and the key's hash code may never
87 : * change.
88 : *
89 : * Calling find(), insert() or remove() on the table invalidates pointers
90 : * to elements in the table, EntryRefs and Iterators.
91 : *
92 : * This hash table is not thread safe.
93 : *
94 : * If you want an insert only hash table Entry should not have a set_deleted()
95 : * method. If the is_deleted() method is inline and always returns false
96 : * it can be inlined away, and there should be no overhead.
97 : *
98 : * @tparam Entry The type of element stored in the table.
99 : * Must conform to the interface mentioned above
100 : * Should be small, a pointer is optimal.
101 : * @tparam Allocator STL style allocator for Entries.
102 : *
103 : * @note
104 : * The design of the hash table is taken from CPython
105 : * as presented in the book `Beautiful Code',
106 : * and Google's sparsehash (https://code.google.com/p/sparsehash)
107 : *
108 : * @Cpp11 Use C++11 move semantics to move entries on resize instead of copying them
109 : */
110 : template<typename _Entry,
111 : typename _Allocator = MemoryAllocator<_Entry> >
112 : struct HashTable {
113 : typedef _Entry Entry;
114 : typedef _Allocator Allocator;
115 :
116 : static const size_t DEFAULT_INITIAL_CAPACITY = 256;
117 : static const size_t DEFAULT_LOAD_FACTOR = 85;
118 :
119 : /***
120 : * Creates an empty hash table.
121 : *
122 : * @Cpp11 Make this a constexpr
123 : */
124 1304 : HashTable() : entries(0),
125 : capacity(0),
126 : count(0),
127 : deleted(0),
128 : threshold(0),
129 1304 : load_factor(DEFAULT_LOAD_FACTOR) {}
130 :
131 72070 : HashTable(size_t initial_capacity, size_t load_factor = DEFAULT_LOAD_FACTOR) {
132 72070 : assert(initial_capacity > 0);
133 :
134 72070 : this->capacity = initial_capacity;
135 72070 : this->count = 0;
136 72070 : this->load_factor = load_factor;
137 :
138 72070 : allocate_table();
139 72070 : };
140 :
141 71906 : ~HashTable() { destroy(entries, capacity); }
142 :
143 : /***
144 : * A reference to an entry in the table.
145 : *
146 : * Can be obtained via find(), and used with insert() and remove()
147 : * for insertion and deletion with a single hash lookup.
148 : */
149 : class EntryRef {
150 : public:
151 : /***
152 : * Check if entry pointed to by ref is valid.
153 : *
154 : * Accessing an invalid entry leads to undefined behaviour.
155 : */
156 11231201 : operator bool() const { return occupied; }
157 :
158 : /***
159 : * Access to refs entry.
160 : * Only use this if operator bool() returns true
161 : *
162 : * You MUST NOT alter the key of entry
163 : */
164 9327665 : Entry *operator->() { return entry; }
165 7827672 : Entry &operator*() { return *entry; }
166 : private:
167 10731969 : EntryRef(Entry *e, bool occupied) : entry(e), occupied(occupied) {}
168 :
169 : Entry *entry;
170 : bool occupied;
171 :
172 : friend struct HashTable;
173 : };
174 :
175 : /***
176 : * An STL style iterator that can be used to traverse all
177 : * elements stored in the table
178 : */
179 : class Iterator {
180 : public:
181 : bool operator==(Iterator it) const { return pos == it.pos; }
182 456626 : bool operator!=(Iterator it) const { return pos != it.pos; }
183 :
184 420673 : Iterator& operator++() {
185 420673 : pos = next_occupied(pos, end);
186 :
187 420673 : return *this;
188 : }
189 : Iterator operator++(int) {
190 : Iterator it(*this);
191 : ++(*this);
192 : return it;
193 : }
194 :
195 0 : Entry& operator*() { return *pos; }
196 841346 : Entry* operator->() { return pos; }
197 : private:
198 71906 : Iterator(Entry *pos, Entry *end) : pos(pos), end(end) {}
199 :
200 : Entry *pos, *end;
201 :
202 : friend struct HashTable;
203 : };
204 :
205 : /***
206 : * Find entry for key.
207 : * Iff no such entry exists converting the returned ref to bool yields false.
208 : * The returned EntryRef can be used to insert or remove items.
209 : *
210 : * T must have a member function:
211 : * size_t hash() const;
212 : * T must be equality comparable to an Entry
213 : */
214 : template<typename T>
215 10731969 : EntryRef find(const T& t) {
216 : // make sure table has at least one unoccupied entry
217 10731969 : if ((count + deleted) >= threshold)
218 680 : grow_table(capacity + 1);
219 :
220 : EXPENSIVE_ASSERT(has_empty_entries());
221 :
222 10731969 : size_t hash = t.hash();
223 10731969 : size_t index = hash;
224 10731969 : size_t perturb = hash;
225 :
226 10731969 : Entry *insert_pos = NULL;
227 10731969 : bool occupied = false;
228 :
229 11324021 : while (1) {
230 22055990 : Entry *e = entries + fast_modulo(index, capacity);
231 :
232 22055990 : if (e->is_empty()) {
233 3714904 : if (insert_pos) {
234 : // No entry for key found, could be inserted at insert_pos
235 0 : break;
236 : } else {
237 : // No entry for key found, could be inserted at e
238 3714904 : insert_pos = e;
239 3714904 : break;
240 : }
241 18341086 : } else if (e->is_deleted() && !insert_pos) {
242 : // Not the entry we're looking for, but we could insert here
243 0 : insert_pos = e;
244 18341086 : } else if (*e == t) {
245 : // Found entry
246 7017065 : insert_pos = e;
247 7017065 : occupied = true;
248 7017065 : break;
249 : }
250 :
251 : // e does not contain the entry we are looking for
252 : // or a deleted entry, keep searching
253 11324021 : index = (5 * index) + 1 + perturb;
254 11324021 : perturb >>= 5;
255 : }
256 :
257 : // single point of return for RVO
258 10731969 : return EntryRef(insert_pos, occupied);
259 : }
260 :
261 : /***
262 : * Insert element into table.
263 : * Does nothing if it is already present.
264 : *
265 : * Returns the newly inserted or already present element.
266 : *
267 : * T must have a member function:
268 : * size_t hash() const;
269 : * T must be equality comparable to an Entry
270 : * T must be assignable to an Entry
271 : */
272 : template<typename T>
273 7328440 : Entry& insert(const T& t) {
274 7328440 : return insert(find(t), t);
275 : }
276 :
277 : /***
278 : * Insert into entry pointed to by ref.
279 : * Does nothing if it is already present.
280 : *
281 : * Returns the newly inserted or already present element.
282 : *
283 : * `e' MUST have the same key as was used to obtain `ref'.
284 : *
285 : * T must have a member function:
286 : * size_t hash() const;
287 : * T must be equality comparable to an Entry
288 : * T must be assignable to an Entry
289 : */
290 : template<typename T>
291 7827672 : Entry& insert(EntryRef ref, const T& t) {
292 7827672 : if (ref) {
293 4113539 : return *ref;
294 : } else {
295 3714133 : return update(ref, t);
296 : }
297 : }
298 :
299 : /***
300 : * Insert or update element in table.
301 : *
302 : * Returns the newly inserted element.
303 : *
304 : * T must have a member function:
305 : * size_t hash() const;
306 : * T must be equality comparable to an Entry
307 : * T must be assignable to an Entry
308 : */
309 : template<typename T>
310 : Entry& update(const T& t) {
311 : return update(find(t), t);
312 : }
313 :
314 : /***
315 : * Insert or update element in table.
316 : *
317 : * Returns the newly inserted element.
318 : *
319 : * `e' MUST have the same key as was used to obtain `ref'.
320 : *
321 : * T must have a member function:
322 : * size_t hash() const;
323 : * T must be equality comparable to an Entry
324 : * T must be assignable to an Entry
325 : */
326 : template<typename T>
327 3714133 : Entry& update(EntryRef ref, const T& t) {
328 3714133 : if (ref->is_empty()) {
329 : // insert into empty entry
330 3714133 : count++;
331 0 : } else if (ref->is_deleted()) {
332 : // overwrite deleted entry
333 0 : count++;
334 0 : deleted--;
335 : }
336 :
337 3714133 : ref->set_occupied(t);
338 :
339 3714133 : return *ref;
340 : }
341 :
342 : /**
343 : * Utility function for hashtables that store key value pairs
344 : */
345 : template<typename Key, typename Val>
346 : Entry& insert(const Key& k, const Val& v) {
347 : return insert(find(k), Entry(k, v));
348 : }
349 :
350 : /**
351 : * Utility function for hashtables that store key value pairs
352 : */
353 : template<typename Key, typename Val>
354 499232 : Entry& insert(EntryRef ref, const Key& k, const Val& v) {
355 499232 : return insert(ref, Entry(k, v));
356 : }
357 :
358 : /**
359 : * Utility function for hashtables that store key value pairs
360 : */
361 : template<typename Key, typename Val>
362 : Entry& update(const Key& k, const Val& v) {
363 : return update(find(k), Entry(k, v));
364 : }
365 :
366 : /**
367 : * Utility function for hashtables that store key value pairs
368 : */
369 : template<typename Key, typename Val>
370 : Entry& update(EntryRef ref, const Key& k, const Val& v) {
371 : return update(ref, Entry(k, v));
372 : }
373 :
374 : /***
375 : * Remove entry pointed to by ref.
376 : *
377 : * `e' MUST have the same key as was used to obtain `ref'.
378 : *
379 : * T must have a member function:
380 : * size_t hash() const;
381 : * T must be equality comparable to an Entry
382 : */
383 : void remove(EntryRef ref) {
384 : if (ref) {
385 : ref->set_deleted();
386 :
387 : count--;
388 : deleted++;
389 : }
390 : }
391 :
392 : /***
393 : * Remove element from table.
394 : *
395 : * T must have a member function:
396 : * size_t hash() const;
397 : * T must be equality comparable to an Entry
398 : */
399 : template<typename T>
400 : void remove(const T& t) {
401 : remove(find(t));
402 : }
403 :
404 : /***
405 : * Remove all entries from the table.
406 : */
407 : void clear() {
408 : count = 0;
409 : deleted = 0;
410 :
411 : for (Entry *e = entries, *end = entries + capacity; e != end; ++e) {
412 : e->set_empty();
413 : }
414 : }
415 :
416 : /***
417 : * Makes sure the table has enough space for at least n entries
418 : */
419 1304 : void reserve(size_t n) {
420 1304 : if (n <= capacity)
421 0 : return;
422 :
423 1304 : grow_table(n);
424 : }
425 :
426 : /***
427 : * Change the tables load factor.
428 : * This never triggers a resizing, a resize may be triggered by the next
429 : * call to find, insert, update or erase.
430 : */
431 1304 : void set_load_factor(size_t load_factor) {
432 1304 : assert(load_factor > 0);
433 1304 : assert(load_factor < 100);
434 :
435 1304 : this->load_factor = load_factor;
436 1304 : update_threshold();
437 1304 : }
438 :
439 : /***
440 : * Access to all occupied entries in table.
441 : */
442 35953 : Iterator begin() {
443 35953 : Entry *begin = entries;
444 35953 : Entry *end = entries + capacity;
445 :
446 : // skip to first entry that is occupied
447 35953 : if (begin != end && !begin->is_occupied())
448 34666 : begin = next_occupied(begin, end);
449 :
450 35953 : return Iterator(begin, end);
451 : }
452 :
453 35953 : Iterator end() {
454 35953 : return Iterator(entries + capacity, entries + capacity);
455 : }
456 :
457 456626 : size_t size() { return count; }
458 : bool empty() { return count == 0; }
459 : private:
460 : HashTable(const HashTable&); // non-copyable
461 : HashTable& operator=(const HashTable&); // non-assignable
462 :
463 : bool has_empty_entries() {
464 : return capacity > (count + deleted);
465 : }
466 :
467 : /**
468 : * Increase capacity, re-allocate table and copy over all old entries
469 : */
470 1984 : void grow_table(size_t new_capacity) {
471 1984 : Entry *old_entries = entries;
472 1984 : size_t old_capacity = capacity;
473 :
474 1984 : capacity = next_power_of_two(new_capacity);
475 :
476 1984 : assert(capacity > 0);
477 1984 : assert(capacity > old_capacity);
478 1984 : allocate_table();
479 :
480 : // re-insert all entries
481 2620352 : for (Entry *it = old_entries, *end = old_entries + old_capacity; it != end; ++it) {
482 2618368 : if (it->is_occupied()) {
483 : // We use an optimized version of the hash lookup here.
484 : // We can omit some checks since we know that,
485 : // a) the table contains no deleted entries,
486 : // b) every entry we insert has a unique key
487 :
488 2225270 : size_t hash = it->hash();
489 2225270 : size_t index = hash;
490 2225270 : size_t perturb = hash;
491 :
492 807811 : while (1) {
493 3033081 : Entry& e = entries[fast_modulo(index, capacity)];
494 :
495 3033081 : if (e.is_empty()) {
496 2225270 : e.set_occupied(*it);
497 2225270 : break;
498 : }
499 :
500 : // e does not contain the entry we are looking for
501 : // or a deleted entry, keep searching
502 807811 : index = (5 * index) + 1 + perturb;
503 807811 : perturb >>= 5;
504 : }
505 : }
506 : }
507 :
508 : // free old table
509 1984 : destroy(old_entries, old_capacity);
510 1984 : }
511 :
512 : /***
513 : * Allocate table with given capacity.
514 : * Also recomputes resize threshold
515 : */
516 74054 : void allocate_table() {
517 74054 : assert(is_power_of_two(capacity));
518 :
519 74054 : deleted = 0;
520 74054 : update_threshold();
521 :
522 : // allocate new table
523 74054 : entries = allocator.allocate(capacity);
524 74054 : assert(capacity == 0 || entries != NULL);
525 :
526 : // initialize table entries
527 74054 : new (entries) Entry[capacity]();
528 74054 : }
529 :
530 73890 : void destroy(Entry *es, size_t sz) {
531 21100194 : for (Entry *it = es, *end = it + sz; it != end; ++it) {
532 21026304 : it->~Entry();
533 : }
534 :
535 73890 : allocator.deallocate(es, sz);
536 73890 : }
537 :
538 75358 : void update_threshold() {
539 75358 : threshold = (capacity * load_factor) / 100;
540 75358 : }
541 :
542 455339 : static Entry *next_occupied(Entry *current, Entry *end) {
543 455339 : if (current == end) return current;
544 :
545 9203968 : do {
546 9203968 : ++current;
547 : } while (current != end && !current->is_occupied());
548 :
549 455339 : return current;
550 : }
551 :
552 : Entry *entries; // the table of entries
553 : size_t capacity; // number of elements the table can hold
554 : size_t count; // number of elements stored in the table
555 : size_t deleted; // number of deleted elements in the table
556 : size_t threshold; // table will be resized when `count' gets
557 : // greater than `threshold'
558 : size_t load_factor; // used to compute threshold
559 : Allocator allocator; // used to allocate table
560 : };
561 :
562 :
563 : /***
564 : * A HashTable entry that uses Utf8Strings as key.
565 : * Useful for using a HashTable as a hash map.
566 : *
567 : * @code
568 : * typedef HashTable<NameValueEntry<int> > HashMap;
569 : *
570 : * HashMap map;
571 : * Utf8String key = ...;
572 : *
573 : * map.insert(key, 5);
574 : *
575 : * int i = *map.find(key);
576 : * @endcode
577 : *
578 : * The NULL string is forbidden as key
579 : *
580 : * @Cpp11 Add a templated typedef of HashTable that makes using it as a hash map easier.
581 : */
582 : template<typename Value>
583 : struct NameValuePair {
584 : NameValuePair() {}
585 : NameValuePair(Utf8String name, Value v) : _name(name), _value(v) {}
586 :
587 : Utf8String name() const { return _name; }
588 : Value& value() { return _value; }
589 : const Value& value() const { return _value; }
590 : private:
591 : Utf8String _name;
592 : Value _value;
593 :
594 : /// Interface to HashTable
595 :
596 : size_t hash() const { return _name.hash(); }
597 :
598 : bool is_empty() const { return _name == (utf*) 0; }
599 : bool is_deleted() const { return _name == (utf*) 1; }
600 : bool is_occupied() const { return _name > (utf*) 1; }
601 :
602 : void set_empty() { _name = (utf*) 0; }
603 : void set_deleted() { _name = (utf*) 1; }
604 : void set_occupied(const NameValuePair& n) { (*this) = n; }
605 :
606 : bool operator==(Utf8String u) const { return _name == u; }
607 : bool operator==(const NameValuePair& n) const { return _name == n._name; }
608 :
609 : template<typename T, typename A>
610 : friend struct HashTable;
611 : };
612 :
613 : /***
614 : * Same as NameValuePair, but for insert only hash tables.
615 : */
616 : template<typename Value>
617 18407936 : struct InsertOnlyNameValuePair {
618 18407936 : InsertOnlyNameValuePair() {}
619 499232 : InsertOnlyNameValuePair(Utf8String name, Value v) : _name(name), _value(v) {}
620 :
621 420673 : Utf8String key() const { return _name; }
622 2254626 : Value& value() { return _value; }
623 : const Value& value() const { return _value; }
624 :
625 : Value *operator->() { return _value; }
626 : Value &operator*() { return *_value; }
627 : private:
628 : Utf8String _name;
629 : Value _value;
630 :
631 : /// Interface to HashTable
632 :
633 0 : size_t hash() const { return _name.hash(); }
634 :
635 3967205 : bool is_empty() const { return _name == (utf*) 0; }
636 2968741 : bool is_deleted() const { return _name == (utf*) 1; }
637 9203968 : bool is_occupied() const { return _name > (utf*) 1; }
638 :
639 : void set_empty() { _name = (utf*) 0; }
640 : void set_deleted() { _name = (utf*) 1; }
641 499232 : void set_occupied(const InsertOnlyNameValuePair& n) { (*this) = n; }
642 :
643 2968741 : bool operator==(Utf8String u) const { return _name == u; }
644 : bool operator==(const InsertOnlyNameValuePair& n) const { return _name == n._name; }
645 :
646 : template<typename T, typename A>
647 : friend struct HashTable;
648 : };
649 :
650 :
651 : /***
652 : * A HashTable entry that uses a Utf8String as key.
653 : * Useful for using a HashTable as an intrusive hash map.
654 : *
655 : * The key is not stored separately in the entry, Value must store
656 : * and provide access to the key.
657 : *
658 : * The type Value must have members for accessing the key:
659 : * @code
660 : * Utf8String key() const;
661 : * void set_key(Utf8String);
662 : * @endcode
663 : *
664 : * The NULL string is forbidden as key
665 : */
666 : template<typename Value>
667 : struct NamedEntry : Value {
668 : NamedEntry() {}
669 : NamedEntry(const Value& v) : Value(v) {}
670 : private:
671 : using Value::key;
672 : using Value::set_key;
673 :
674 : /// Interface to HashTable
675 :
676 : size_t hash() const { return key().hash(); }
677 :
678 : bool is_empty() const { return key() == (utf*) 0; }
679 : bool is_deleted() const { return key() == (utf*) 1; }
680 : bool is_occupied() const { return key() > (utf*) 1; }
681 :
682 : void set_empty() { set_key((utf*) 0); }
683 : void set_deleted() { set_key((utf*) 1); }
684 : void set_occupied(const NamedEntry& e) { (*this) = e; }
685 :
686 : bool operator==(Utf8String u) const { return key() == u; }
687 : bool operator==(const Value& v) const { return key() == v.key(); }
688 :
689 : template<typename T, typename A>
690 : friend struct HashTable;
691 : };
692 :
693 :
694 : /***
695 : * Same as NamedEntry, but for insert only hash tables.
696 : */
697 : template<typename Value>
698 2503680 : struct InsertOnlyNamedEntry : Value {
699 5175296 : InsertOnlyNamedEntry() {}
700 3381763 : InsertOnlyNamedEntry(const Value& v) : Value(v) {}
701 : // InsertOnlyNamedEntry(Utf8String key, const Value& v) : Value(v) { set_key(key); }
702 : private:
703 : using Value::key;
704 : using Value::set_key;
705 :
706 : /// Interface to HashTable
707 :
708 2127802 : size_t hash() const { return key().hash(); }
709 :
710 8077887 : bool is_empty() const { return key() == (utf*) 0; }
711 2737395 : bool is_deleted() const { return false; }
712 2503680 : bool is_occupied() const { return key() != (utf*) 0; }
713 :
714 : void set_empty() { set_key((utf*) 0); }
715 3381763 : void set_occupied(const Value& v) { (*this) = v; }
716 :
717 46917 : bool operator==(Utf8String u) const { return key() == u; }
718 2690478 : bool operator==(const Value& v) const { return key() == v.key(); }
719 :
720 : template<typename T, typename A>
721 : friend struct HashTable;
722 : };
723 :
724 :
725 : /* data structures for hashtables ********************************************
726 :
727 : All utf-symbols, javastrings and classes are stored in global
728 : hashtables, so every symbol exists only once. Equal symbols have
729 : identical pointers. The functions for adding hashtable elements
730 : search the table for the element with the specified name/text and
731 : return it on success. Otherwise a new hashtable element is created.
732 :
733 : The hashtables use external linking for handling collisions. The
734 : hashtable structure contains a pointer <ptr> to the array of
735 : hashtable slots. The number of hashtable slots and therefore the
736 : size of this array is specified by the element <size> of hashtable
737 : structure. <entries> contains the number of all hashtable elements
738 : stored in the table, including those in the external chains. The
739 : hashtable element structures (utf, literalstring, classinfo)
740 : contain both a pointer to the next hashtable element as a link for
741 : the external hash chain and the key of the element. The key is
742 : computed from the text of the string or the classname by using up
743 : to 8 characters.
744 :
745 : If the number of entries in the hashtable exceeds twice the size of
746 : the hashtableslot-array it is supposed that the average length of
747 : the external chains has reached a value beyond 2. Therefore the
748 : functions for adding hashtable elements (utf_new, class_new,
749 : literalstring_new) double the hashtableslot-array. In this
750 : restructuring process all elements have to be inserted into the new
751 : hashtable and new external chains must be built.
752 :
753 : Example for the layout of a hashtable:
754 :
755 : hashtable.ptr-->+-------------------+
756 : | |
757 : ...
758 : | |
759 : +-------------------+ +-------------------+ +-------------------+
760 : | hashtable element |-->| hashtable element |-->| hashtable element |-->NULL
761 : +-------------------+ +-------------------+ +-------------------+
762 : | hashtable element |
763 : +-------------------+ +-------------------+
764 : | hashtable element |-->| hashtable element |-->NULL
765 : +-------------------+ +-------------------+
766 : | hashtable element |-->NULL
767 : +-------------------+
768 : | |
769 : ...
770 : | |
771 : +-------------------+
772 :
773 : */
774 :
775 : /* hashtable ******************************************************************/
776 :
777 : struct hashtable {
778 : Mutex *mutex; /* required for locking */
779 : u4 size; /* current size of the hashtable */
780 : u4 entries; /* number of entries in the table */
781 : void **ptr; /* pointer to hashtable */
782 : };
783 :
784 :
785 : /* function prototypes ********************************************************/
786 :
787 : /* create hashtable */
788 : void hashtable_create(hashtable *hash, u4 size);
789 :
790 : /* creates and resizes a hashtable */
791 : hashtable *hashtable_resize(hashtable *hash, u4 size);
792 :
793 : /* frees a hashtable */
794 : void hashtable_free(hashtable *hash);
795 :
796 : #endif // HASHTABLE_HPP_
797 :
798 :
799 : /*
800 : * These are local overrides for various environment variables in Emacs.
801 : * Please do not remove this and leave it at the end of the file, where
802 : * Emacs will automagically detect them.
803 : * ---------------------------------------------------------------------
804 : * Local variables:
805 : * mode: c++
806 : * indent-tabs-mode: t
807 : * c-basic-offset: 4
808 : * tab-width: 4
809 : * End:
810 : * vim:noexpandtab:sw=4:ts=4:
811 : */
|