├── .gitignore ├── LICENSE ├── Makefile ├── README ├── hhash.c ├── hhash.h ├── hhmap.c ├── hhmap.h ├── hhset.c ├── hhset.h ├── mach.c ├── man ├── hhash.3 ├── hhmap.3 └── hhset.3 └── test ├── map ├── test1.c ├── test2.c └── test3.c ├── set ├── test1.c ├── test2.c └── test3.c ├── test.h ├── test1.c ├── test2.c ├── test3.c ├── test4.c ├── test5.c └── test6.c /.gitignore: -------------------------------------------------------------------------------- 1 | [^/]+\.o 2 | o\.[^/]+$ 3 | 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010 Jani Lahtinen 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OFILES=\ 2 | hhash.o\ 3 | hhset.o\ 4 | hhmap.o\ 5 | 6 | CFILES=\ 7 | hhash.c\ 8 | mach.c\ 9 | hhset.c\ 10 | hhmap.c\ 11 | 12 | HFILES=\ 13 | hhash.h\ 14 | hhset.h\ 15 | hhmap.h\ 16 | 17 | TESTC=${shell find test -name "*.c"} 18 | 19 | TESTS=${TESTC:%.c=%} 20 | 21 | default: $(OFILES) libhhash.a 22 | 23 | $(OFILES): $(CFILES) $(HFILES) 24 | 25 | CFLAGS+=-std=c99 -O3 26 | 27 | .PHONY: format 28 | format: $(CFILES) $(HFILES) 29 | clang-format -style=Google -i $^ 30 | 31 | libhhash.a: $(OFILES) 32 | ar rcs $@ $^ 33 | 34 | .PHONY: clean 35 | clean: 36 | rm -f $(OFILES) libhhash.a $(TESTS) 37 | 38 | $(TESTS): $(TESTC) $(CFILES) $(HFILES) libhhash.a 39 | cc -L. -lhhash $(CFLAGS) -I. -Itest -o $@ $@.c 40 | 41 | .PHONY: test 42 | test: $(TESTS) 43 | @for f in $(TESTS); \ 44 | do \ 45 | echo $$f; \ 46 | ./$$f; \ 47 | done; 48 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | HOPSCOTCH HASH TABLE IMPLMENTATION 2 | 3 | Hash tables are very efficient for key value lookup in both speed and 4 | memory usage. Hopscotch hash tables also provide memory locality and 5 | cache efficiency by using open addressing and a special method of 6 | linear probing. 7 | 8 | 9 | IMPLEMENTATION NOTES 10 | 11 | The neighborhood of baskets is circular going back after the last 12 | element to the first using modulo arithmetic's. Wherever we use a 13 | difference (x-y)%n, in order to keep modulo semantics using unsigned 14 | variables we add n: (n+x-y)%n, which is basically a nop but guarantees 15 | that the result of the subtraction remains positive. 16 | 17 | We are using the following GCC builtin function to calculate an integer 18 | logarithm in base 2: 19 | 20 | __builtin_clz, count-leading-zeros, returns the number of leading 21 | 0-bits in x, starting at the most significant bit position. If x is 0, 22 | the result is undefined. 23 | 24 | 25 | BUILDING 26 | 27 | The code relies, besides the aforementioned builtins, on stdlib.h for the 28 | function malloc. 29 | 30 | 31 | TESTING 32 | 33 | Unit tests can be found from the directory test. To run them use 'make test' 34 | target. The tests do not print anything but abort if something has not been as 35 | it should. 36 | 37 | 38 | DOCUMENTATION 39 | 40 | A Unix manual page is in the directory man. This also provides the 41 | documentation for the provided functions. 42 | 43 | 44 | CONTACT 45 | 46 | Jani Lahtinen 47 | 48 | 49 | REFERENCES 50 | 51 | Herlihy, Maurice and Shavit, Nir and Tzafrir, Moran, Hopscotch 52 | Hashing. DISC '08: Proceedings of the 22nd international symposium on 53 | Distributed Computing. Arcachon, France: 54 | Springer-Verlag. pp. 350--364, 2008. 55 | -------------------------------------------------------------------------------- /hhash.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hhash.h" 3 | #include "mach.c" 4 | 5 | #define mod(x, n) ((x) < (n) ? (x) : (x) - (n)) 6 | 7 | HHash *hhashnew(ulong n) { 8 | HHash *T = malloc(sizeof(HHash)); 9 | T->m = 0; 10 | T->n = n; 11 | T->k = fls(n - 1); 12 | T->V = malloc(n * sizeof(ulong)); 13 | T->H = malloc(n * sizeof(ulong)); 14 | for (int i = 0; i < n; ++i) { 15 | T->H[i] = 0; 16 | T->V[i] = 0; 17 | } 18 | return T; 19 | } 20 | 21 | void hhashfree(HHash *T) { 22 | free(T->V); 23 | free(T->H); 24 | free(T); 25 | } 26 | 27 | static int next(HHash *T, ulong h, int i) { 28 | ulong H = T->H[h] & (~0 << i); 29 | if (H == 0) return -1; 30 | return ffs(H); 31 | } 32 | 33 | static int succ(HHash *T, ulong h, ulong i) { 34 | if (get(T->H[h], i)) return i; 35 | return next(T, h, i); 36 | } 37 | 38 | int hhashsucc(HHash *T, ulong h, ulong i) { 39 | h %= T->n; 40 | return succ(T, h, i); 41 | } 42 | 43 | static void move(HHash *T, ulong h, ulong i, ulong j) { 44 | unset(T->H[h], i); 45 | set(T->H[h], j); 46 | i = mod(h + i, T->n); 47 | j = mod(h + j, T->n); 48 | ulong v = T->V[i]; 49 | T->V[i] = 0; 50 | T->V[j] = v; 51 | } 52 | 53 | void hhashdel(HHash *T, ulong h, ulong i) { 54 | h %= T->n; 55 | ulong j = mod(h + i, T->n); 56 | if (T->V[j] == 0 || !get(T->H[h], i)) return; 57 | T->V[j] = 0; 58 | unset(T->H[h], i); 59 | --T->m; 60 | } 61 | 62 | static ulong probe(HHash *T, ulong h) { 63 | int i = 0; 64 | for (; h + i < T->n; ++i) 65 | if (T->V[h + i] == 0) return i; 66 | ulong j = 0; 67 | for (; T->V[j] != 0; ++j) 68 | ; 69 | return i + j; 70 | } 71 | 72 | static ulong seek(HHash *T, ulong h) { 73 | for (ulong i = T->k - 1; i > 0; --i) { 74 | ulong hi = mod(T->n + h - i, T->n); 75 | if (T->H[hi] != 0 && ffs(T->H[hi]) < i) return i; 76 | } 77 | return 0; 78 | } 79 | 80 | int hhashput(HHash *T, ulong h, ulong v) { 81 | if (T->m == T->n || v == 0) return 0; 82 | h %= T->n; 83 | ulong d = probe(T, h); 84 | while (d >= T->k) { 85 | ulong hd = mod(h + d, T->n); 86 | ulong z = seek(T, hd); 87 | if (z == 0) return 0; 88 | ulong j = z; 89 | z = mod(T->n + hd - z, T->n); 90 | ulong i = succ(T, z, 0); 91 | move(T, z, i, j); 92 | d = mod(T->n + z + i - h, T->n); 93 | } 94 | ulong hd = mod(h + d, T->n); 95 | T->V[hd] = v; 96 | set(T->H[h], d); 97 | ++T->m; 98 | return 1; 99 | } 100 | -------------------------------------------------------------------------------- /hhash.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBHHASH_H 2 | #define LIBHHASH_H 3 | 4 | typedef unsigned long ulong; 5 | typedef struct HHash HHash; 6 | 7 | struct HHash { 8 | ulong k, m, n; 9 | ulong *H; 10 | ulong *V; 11 | }; 12 | 13 | #define hhashget(hhash, x, i) ((hhash)->V[((x) + (i)) % (hhash)->n]) 14 | 15 | HHash *hhashnew(ulong); 16 | void hhashfree(HHash *); 17 | int hhashput(HHash *, ulong, ulong); 18 | void hhashdel(HHash *, ulong, ulong); 19 | int hhashsucc(HHash *, ulong, ulong); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /hhmap.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hhash.h" 3 | #include "hhset.h" 4 | #include "hhmap.h" 5 | 6 | #define HALF (8 * sizeof(uint)) 7 | 8 | static ulong id(ulong x) { return (uint)x; } 9 | 10 | static int neq(ulong x, ulong y) { return (uint)x != (uint)y; } 11 | 12 | HHSet *hhmapnew(ulong n, ulong (*hash)(ulong), int (*cmp)(ulong, ulong)) { 13 | HHSet *S = malloc(sizeof(HHSet)); 14 | S->T = hhashnew(n); 15 | S->hash = (hash != NULL) ? hash : id; 16 | S->cmp = (cmp != NULL) ? cmp : neq; 17 | return S; 18 | } 19 | 20 | int hhmapput(HHSet *S, uint k, uint v) { 21 | if (v == 0) return 0; 22 | ulong x = (((ulong)v) << HALF) | k; 23 | return hhsetput(S, x); 24 | } 25 | 26 | uint hhmapget(HHSet *S, uint k) { return (uint)(hhsetget(S, k) >> HALF); } 27 | 28 | uint hhmapdel(HHSet *S, uint k) { return (uint)(hhsetdel(S, k) >> HALF); } 29 | -------------------------------------------------------------------------------- /hhmap.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBHHMAP_H 2 | #define LIBHHMAP_H 3 | 4 | typedef unsigned int uint; 5 | 6 | HHSet *hhmapnew(ulong n, ulong (*)(ulong), int (*)(ulong, ulong)); 7 | int hhmapput(HHSet *, uint, uint); 8 | uint hhmapget(HHSet *, uint); 9 | uint hhmapdel(HHSet *, uint); 10 | 11 | #endif 12 | -------------------------------------------------------------------------------- /hhset.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hhash.h" 3 | #include "hhset.h" 4 | 5 | static ulong id(ulong x) { return x - 1; } 6 | 7 | static int neq(ulong x, ulong y) { return x != y; } 8 | 9 | HHSet *hhsetnew(ulong n, ulong (*hash)(ulong), int (*cmp)(ulong, ulong)) { 10 | HHSet *S = malloc(sizeof(HHSet)); 11 | S->T = hhashnew(n); 12 | S->hash = (hash != NULL) ? hash : id; 13 | S->cmp = (cmp != NULL) ? cmp : neq; 14 | return S; 15 | } 16 | 17 | void hhsetfree(HHSet *S) { 18 | hhashfree(S->T); 19 | free(S); 20 | } 21 | 22 | int hhsetcopy(HHSet *S, HHSet *T) { 23 | for (int i = 0; i < S->T->n; ++i) { 24 | ulong x = S->T->V[i]; 25 | if (x) { 26 | ulong h = T->hash(x); 27 | if (!hhashput(T->T, h, x)) return 0; 28 | } 29 | } 30 | return 1; 31 | } 32 | 33 | static ulong hunt(HHSet *S, ulong h, ulong k, int kill) { 34 | int i = hhashsucc(S->T, h, 0); 35 | while (i >= 0) { 36 | ulong x = hhashget(S->T, h, i); 37 | if (S->cmp(k, x) == 0) { 38 | if (kill) hhashdel(S->T, h, i); 39 | return x; 40 | } 41 | i = hhashsucc(S->T, h, i + 1); 42 | } 43 | return 0; 44 | } 45 | 46 | ulong hhsetget(HHSet *S, ulong k) { return hunt(S, S->hash(k), k, 0); } 47 | 48 | ulong hhsetdel(HHSet *S, ulong k) { return hunt(S, S->hash(k), k, 1); } 49 | 50 | static int resize(HHSet *R) { 51 | HHSet *S = hhsetnew(2 * R->T->n, R->hash, R->cmp); 52 | if (!hhsetcopy(R, S)) { 53 | hhsetfree(S); 54 | return 0; 55 | } 56 | HHash *T = R->T; 57 | R->T = S->T; 58 | S->T = T; 59 | hhsetfree(S); 60 | return 1; 61 | } 62 | 63 | int hhsetput(HHSet *S, ulong x) { 64 | if (x == 0) return 0; 65 | ulong h = S->hash(x); 66 | if (hunt(S, h, x, 0)) return 1; 67 | if (hhashput(S->T, h, x)) return 1; 68 | if (!resize(S)) return 0; 69 | return hhashput(S->T, h, x); 70 | } 71 | -------------------------------------------------------------------------------- /hhset.h: -------------------------------------------------------------------------------- 1 | #ifndef LIBHHSET_H 2 | #define LIBHHSET_H 3 | 4 | typedef struct HHSet HHSet; 5 | 6 | struct HHSet { 7 | HHash* T; 8 | ulong (*hash)(ulong); 9 | int (*cmp)(ulong, ulong); 10 | }; 11 | 12 | HHSet* hhsetnew(ulong, ulong (*)(ulong), int (*)(ulong, ulong)); 13 | void hhsetfree(HHSet*); 14 | int hhsetcopy(HHSet*, HHSet*); 15 | int hhsetput(HHSet*, ulong); 16 | ulong hhsetget(HHSet*, ulong); 17 | ulong hhsetdel(HHSet*, ulong); 18 | 19 | #endif 20 | -------------------------------------------------------------------------------- /mach.c: -------------------------------------------------------------------------------- 1 | #define WORD (8 * sizeof(ulong)) 2 | 3 | #define clz(x) __builtin_clzl(x) 4 | 5 | #define fls(x) (WORD - clz(x)) 6 | 7 | #define ffs(x) (__builtin_ctzl(x)) 8 | 9 | #define get(x, i) ((x) & (1L << (i))) 10 | 11 | #define set(x, i) (x = (x) | (1L << (i))) 12 | 13 | #define unset(x, i) (x = (x) & ~(1L << (i))) 14 | -------------------------------------------------------------------------------- /man/hhash.3: -------------------------------------------------------------------------------- 1 | .TH HHASH 3 2 | .SH NAME 3 | HHash, hashget, hashnew, hashfree, hashput, hashdel, hashsucc \- 4 | Hopscotch Hash Tables. 5 | .SH SYNOPSIS 6 | .ft L 7 | .nf 8 | #include 9 | .fi 10 | .PP 11 | .ft L 12 | .nf 13 | .ta \w'\fL1234'u +\w'\fL1234'u 14 | typedef struct HHash 15 | { 16 | 17 | ulong *H; 18 | ulong *V; 19 | ulong k, m, n; 20 | }; 21 | .fi 22 | .PP 23 | .ft L 24 | .nf 25 | #define hashget(hhash,h,i) \\ 26 | ((hhash)->V[((h)+(i))%(hhash)->n]) 27 | .fi 28 | .PP 29 | .nf 30 | .ft L 31 | .ta \w'\fL1234'u 32 | HHash *hhashnew(ulong m); 33 | void hhashfree(HHash *T); 34 | int hhashsucc(HHash *T, ulong h, ulong i); 35 | int hhashput(HHash *T, ulong h, ulong x); 36 | void hhashdel(HHash *T, ulong h, ulong i); 37 | .fi 38 | .SH DESCRIPTION 39 | Hopscotch hash table provides a simple hash table implementation that 40 | resolves hash collisions with linear probing from the initial hash 41 | entry up to 42 | .I log(n) 43 | entries ahead, where 44 | .I n 45 | is the size of the hash table. 46 | .PP 47 | Usage of this structure needs evaluating the hash values of the 48 | elements before insertion. The functions always treats the hash values 49 | .I modulo T->n. 50 | .PP 51 | These hash tables are useful when the values to be stored fit in 52 | .I ulong. 53 | With larger entries you are better off using normal hash tables with 54 | linked list buckets, which is then fast enough and simpler to implement. 55 | With integer entries these tables have the following advantages: 56 | .IP 1) 57 | Memory locality and compactness and thus cache efficiency. 58 | .IP 2) 59 | Serialization. The table is contained in the arrays 60 | .I A 61 | and 62 | .I V. 63 | .IP 3) 64 | Concurrency by dividing the array into segments each with locks, though this has not been implemented here. 65 | .PP 66 | .I HHash 67 | is a stucture for the hash table, where 68 | .I H 69 | is the array of 70 | .I hops 71 | and 72 | .I V 73 | is an array containing the values stored in the 74 | table. Zero values are considered empty slots and thus zero cannot be 75 | inserted into the table. Each entry in 76 | .I H 77 | is a bitmap such that if for a hash value 78 | .I h 79 | the bit at 80 | .I i 81 | is set the element 82 | .I V[h+i] 83 | also has the same hash value. Also 84 | .I m 85 | is the number of elements stored in the table, 86 | .I n 87 | is the size of the arrays 88 | .I H 89 | and 90 | .I V. 91 | .I k 92 | is the integer logarithm in base 2 of 93 | .I n. 94 | .I Hhashnew(n) 95 | allocates memory for a hash table with capacity 96 | .I n 97 | and 98 | .I hhashfree 99 | frees it. 100 | .PP 101 | The elements of the hash table are referenced by their hash value and 102 | offset. For a hash value 103 | .I h 104 | the elements with this hash value have offsets 105 | .I 0 <= i < T->k. 106 | .I Hhashsucc(T,h,i) 107 | returns the 108 | .I successor 109 | of the element at 110 | .I V[h+i]. 111 | If the element 112 | .I V[h+i] 113 | has the hash value 114 | .I h 115 | .I hhashsucc 116 | returns 117 | .I i, 118 | and if not it returns the next largest 119 | .I i < T->k 120 | such that 121 | .I V[h+i] 122 | has the hash value 123 | .I h. 124 | If no such 125 | .I i 126 | exists 127 | .I hhashsucc 128 | returns a negative value. Thus 129 | .I V[h+i] 130 | for any 131 | .I i 132 | does not necessarily have the hash value 133 | .I h. 134 | .I Hhashsucc 135 | can be used for both checking if an element is contained in the table 136 | as well as iterating over the elements with the same hash value. 137 | .PP 138 | .I Hhashput(T,h,x) 139 | tries to insert the element 140 | .I x 141 | with hash value 142 | .I h 143 | into the table 144 | .I T. 145 | On successful addition it returns a non-zero value and zero otherwise. 146 | If an attempt to add an element is not successful the table should be 147 | resized, 148 | .I x 149 | is not added to the table, but some other modifications may have taken 150 | place. No operation has been provided here for resizing but one can 151 | simple allocate a new table with a larger size and copy all values from 152 | the current table into the new one. Note, trying to insert zero will not 153 | succeed. This is considered semantically correct behaviour as after the 154 | insertion zero will not be found from the table by 155 | .I hhsucc. 156 | .PP 157 | .I Hhashdel(T,h,i) 158 | removes the element at 159 | .I V[h+i] 160 | provided that 161 | .I V[h+i] 162 | has the hash value 163 | .I h 164 | and 165 | .I 0 <= i < T->k, 166 | if not 167 | .I hhashdel 168 | does nothing. 169 | .SH EXAMPLE 170 | .nf 171 | .ft L 172 | .ta \w'\fL1234'u +\w'\fL1234'u 173 | ulong n = 256; 174 | HHash *T = hhashnew(n); 175 | ulong x = rand(); 176 | ulong h = x%n; 177 | hhashput(T,h,x); 178 | ulong i = hhashsucc(T,h,0); 179 | ulong y = hhashget(T,h,i); 180 | hhashfree(T); 181 | .fi 182 | .SH SEE ALSO 183 | hhset(3), hhmap(3) 184 | .SH REFERENCES 185 | .B Herlihy, Maurice and Shavit, Nir and Tzafrir, Moran; 186 | Hopscotch Hashing. DISC '08: Proceedings of the 22nd international 187 | symposium on Distributed Computing. Arcachon, France: 188 | Springer-Verlag, 2008. pp. 350--364. 189 | -------------------------------------------------------------------------------- /man/hhmap.3: -------------------------------------------------------------------------------- 1 | .TH HHMap 3 2 | .SH NAME 3 | hhmapput, hhmapget, hhmapdel \- 4 | Integer associative arrays using the Hopscotch hash tables. 5 | .SH SYNOPSIS 6 | .ft L 7 | .nf 8 | #include 9 | #include 10 | #include 11 | .fi 12 | .PP 13 | .ft L 14 | .nf 15 | typedef unsigned short uint; 16 | HHSet *hhmapnew(ulong n, void *hash, void *cmp); 17 | int hhmapput(HHSet *S, uint k, uint v); 18 | uint hhmapget(HHSet *S, uint k); 19 | uint hhmapdel(HHSet *S, uint k); 20 | .fi 21 | .SH DESCRIPTION 22 | This implements an associative map using the Hopscotch hash tables. 23 | The keys and values are limited to 24 | .I short. 25 | The key-value pairs are stored in a 26 | .I HHSet 27 | by encoding the key in the first 16 bits and the value in the last 16 28 | bits. The value can never be zero but the key can be. The functions 29 | .I hhmapput, hhmapget 30 | and 31 | .I hhmapdel 32 | manipulate the set as an association. The memory of a set 33 | .I S 34 | can be freed by calling 35 | .I hhsetfree(S). 36 | The table 37 | .I A 38 | can be copied into a table 39 | .I B 40 | by using 41 | .I hhsetcopy(A,B). 42 | .PP 43 | .I Hhsetnew(n,hash,cmp) 44 | allocates memory for a new 45 | .I HHSet 46 | with the given hash- and comparison function. If hash function is 47 | .I NULL 48 | the default function is identity of the first 16 bits. If the 49 | comparison function is 50 | .I NULL 51 | the default is 52 | . I (uint)x != (uint)y. 53 | .PP 54 | .I Hhmapput(S,k,v) 55 | tries to insert the value 56 | .I v 57 | with the key 58 | .I k. 59 | It returns non-zero on success and zero otherwise. 60 | .PP 61 | .I Hhmapget(S,k) 62 | returns the value associated with the key 63 | .I k 64 | and zero if there is none. 65 | .PP 66 | .I Hhmapdel(S,k) 67 | removes the value associated with 68 | .I k 69 | from the table and returns the value which was stored there. 70 | .SH SEE ALSO 71 | hhash(3), hhset(3) 72 | -------------------------------------------------------------------------------- /man/hhset.3: -------------------------------------------------------------------------------- 1 | .TH HHSET 3 2 | .SH NAME 3 | HHSet, hhsetnew, hhsetfree, hhsetcopy, hhsetput, hhsetget, hhsetdel \- 4 | Integer set using the Hopscotch hash tables. 5 | .SH SYNOPSIS 6 | .ft L 7 | .nf 8 | #include 9 | #include 10 | .fi 11 | .PP 12 | typedef struct HHSet HHSet; 13 | .PP 14 | .ft L 15 | .nf 16 | .ta \w'\fL1234'u +\w'\fL1234'u 17 | struct HHSet 18 | { 19 | HHash *T; 20 | ulong (*hash)(ulong); 21 | int (*cmp)(ulong, ulong); 22 | }; 23 | .fi 24 | .PP 25 | .nf 26 | .ft L 27 | .ta \w'\fL1234'u 28 | HHSet *hhsetnew(ulong n, void *cmp, void *hash); 29 | void hhsetfree(HHSet *S); 30 | int hhsetcopy(HHSet *A, HHSet *B); 31 | int hhsetput(HHSet *S, ulong x); 32 | ulong hhsetget(HHSet *S, ulong x); 33 | ulong hhsetdel(HHSet *S, ulong x); 34 | .fi 35 | .SH DESCRIPTION 36 | .I HHSet 37 | implements integer sets using the Hopscotch hash tables. The actual 38 | hash table is in the element 39 | .I T. 40 | .I Hash 41 | is a reference to a hash function, and 42 | .I cmp(x,y) 43 | is a comparison function which should return zero if 44 | .I x = y. 45 | .PP 46 | .I Hhsetnew(n,hash,cmp) 47 | creates a new hash table with initial size 48 | .I n, 49 | hash function 50 | .I hash 51 | and comparison function 52 | .I cmp. 53 | The memory allocated can be freed by 54 | .I hhsetfree. 55 | .PP 56 | .I Hhsetcopy(A,B) 57 | attempts to copy all the elements from 58 | .I A 59 | into 60 | .I B. 61 | If successful it will a return non-zero value and upon the first failure 62 | it will stop copying and returns zero. 63 | .PP 64 | .I Hhsetput(S,x) 65 | tries to insert 66 | .I x 67 | into the set 68 | .I S. 69 | If the underlying call to 70 | .I hhashput 71 | fails it will try to resize the table into twice larger. If this still fails 72 | it will return zero. Also, 73 | .I hhsetput(S,x) 74 | returns zero if 75 | .I x 76 | was already in the set, or 77 | .I x == 0. 78 | .PP 79 | .I y = hhsetget(S,x) 80 | returns the element 81 | .I y 82 | for which 83 | .I cmp(x,y) == 0. 84 | .I y = hhsetdel(S,x) 85 | removes 86 | .I x 87 | from the set and returns an element 88 | .I y 89 | for which 90 | .I cmp(x,y) == 0. 91 | .SH SEE ALSO 92 | hhash(3), hhmap(3) 93 | -------------------------------------------------------------------------------- /test/map/test1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "test.h" 6 | 7 | int main(void) { 8 | ulong n = 256; 9 | HHSet *S = hhmapnew(n, NULL, NULL); 10 | test(hhmapput(S, 5, 15)); 11 | test(hhmapget(S, 5) == 15); 12 | test(hhmapput(S, 7, 17)); 13 | test(hhmapget(S, 7) == 17); 14 | test(hhmapput(S, 3, 13)); 15 | test(hhmapget(S, 3) == 13); 16 | test(hhmapdel(S, 5) == 15); 17 | test(!hhmapget(S, 5)); 18 | test(hhmapdel(S, 7) == 17); 19 | test(!hhmapget(S, 7)); 20 | test(hhmapdel(S, 3) == 13); 21 | test(!hhmapget(S, 3)); 22 | hhsetfree(S); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /test/map/test2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include "test.h" 6 | 7 | void fill(HHSet *S, ulong m) { 8 | for (uint i = 0; i < m; ++i) { 9 | uint x = rand(); 10 | test(hhmapput(S, x, i + 1)); 11 | test(hhmapget(S, x) == i + 1); 12 | } 13 | } 14 | 15 | int main(void) { 16 | ulong n = 256; 17 | ulong m = 150; 18 | HHSet *S = hhmapnew(n, NULL, NULL); 19 | fill(S, m); 20 | test(m == S->T->m); 21 | hhsetfree(S); 22 | return 0; 23 | } 24 | -------------------------------------------------------------------------------- /test/map/test3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include "test.h" 7 | 8 | ulong hash(ulong x) { 9 | ulong k = (uint)x; 10 | return k * 0x9e3779b1; 11 | } 12 | 13 | void fill(HHSet *S, ulong m) { 14 | for (uint i = 0; i < m; ++i) { 15 | uint x = rand() % (USHRT_MAX - 1) + 1; 16 | test(!hhmapget(S, i)); 17 | test(hhmapput(S, i, x)); 18 | test(hhmapget(S, i) == x); 19 | } 20 | } 21 | 22 | int main(void) { 23 | ulong n = 256; 24 | ulong m = 150; 25 | HHSet *S = hhmapnew(n, hash, NULL); 26 | fill(S, m); 27 | test(m == S->T->m); 28 | hhsetfree(S); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /test/set/test1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "test.h" 5 | 6 | static ulong fill(HHSet *S) { 7 | ulong m = 0; 8 | for (ulong i = 0; i < 128; ++i) { 9 | ulong x = rand() + 1; 10 | if (!hhsetget(S, x)) { 11 | test(hhsetput(S, x)); 12 | ++m; 13 | } 14 | } 15 | return m; 16 | } 17 | 18 | static ulong rnd(HHSet *S) { 19 | HHash *T = S->T; 20 | ulong h = rand() % T->n; 21 | while (T->V[h] == 0) h = (h + 1) % T->n; 22 | return T->V[h]; 23 | } 24 | 25 | static ulong strip(HHSet *S) { 26 | ulong m = 0; 27 | for (; m < 64; ++m) { 28 | ulong x = rnd(S); 29 | hhsetdel(S, x); 30 | test(!hhsetget(S, x)); 31 | } 32 | return m; 33 | } 34 | 35 | int main(void) { 36 | ulong n = 256; 37 | HHSet *S = hhsetnew(n, NULL, NULL); 38 | ulong m = fill(S); 39 | test(m == S->T->m); 40 | m -= strip(S); 41 | test(m == S->T->m); 42 | hhsetfree(S); 43 | return 0; 44 | } 45 | -------------------------------------------------------------------------------- /test/set/test2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "test.h" 5 | 6 | int main(void) { 7 | ulong n = 16; 8 | ulong m = 0; 9 | HHSet *S = hhsetnew(n, NULL, NULL); 10 | for (ulong i = 0; i < 30; ++i) { 11 | ulong x = rand() + 1; 12 | if (!hhsetget(S, x)) { 13 | test(hhsetput(S, x)); 14 | ++m; 15 | } 16 | } 17 | test(S->T->m == m); 18 | hhsetfree(S); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /test/set/test3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include "test.h" 5 | 6 | int x; 7 | 8 | static ulong fill(HHSet *S) { 9 | ulong m = 0; 10 | for (ulong i = 0; i < 128; ++i) { 11 | ulong y = rand() + 1; 12 | if (!hhsetget(S, y)) { 13 | test(hhsetput(S, y)); 14 | ++m; 15 | } 16 | test(hhsetget(S, x)); 17 | } 18 | return m; 19 | } 20 | 21 | int main(void) { 22 | ulong n = 256; 23 | srand(13948824); 24 | HHSet *S = hhsetnew(n, NULL, NULL); 25 | x = rand(); 26 | hhsetput(S, x); 27 | fill(S); 28 | hhsetfree(S); 29 | return 0; 30 | } 31 | -------------------------------------------------------------------------------- /test/test.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define test(x) \ 4 | while(!(x)){ \ 5 | fprintf(stderr, "%s:%d\n", __FILE__, __LINE__); \ 6 | abort(); \ 7 | } 8 | -------------------------------------------------------------------------------- /test/test1.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test.h" 4 | 5 | int main(void) { 6 | ulong n = 16; 7 | HHash *T = hhashnew(n); 8 | ulong x = rand() % n; 9 | test(hhashput(T, x, 1)); 10 | test(hhashsucc(T, x, 0) == 0); 11 | test(hhashput(T, x, 1)); 12 | test(hhashsucc(T, x, 1) == 1); 13 | test(hhashput(T, x, 1)); 14 | test(hhashsucc(T, x, 2) == 2); 15 | test(hhashput(T, x, 1)); 16 | test(hhashsucc(T, x, 3) == 3); 17 | test(!hhashput(T, x, 1)); 18 | hhashfree(T); 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /test/test2.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test.h" 4 | 5 | int main(void) { 6 | ulong n = 16; 7 | HHash *T = hhashnew(n); 8 | ulong x = rand() % n; 9 | test(hhashput(T, x, 1)); 10 | ulong i = hhashsucc(T, x, 0); 11 | test(i == 0); 12 | hhashdel(T, x, i); 13 | test(hhashsucc(T, x, 0) < 0); 14 | hhashfree(T); 15 | return 0; 16 | } 17 | -------------------------------------------------------------------------------- /test/test3.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test.h" 4 | 5 | int main(void) { 6 | ulong n = 16; 7 | HHash *T = hhashnew(n); 8 | ulong x = rand() % n; 9 | test(hhashput(T, x, 1)); 10 | test(hhashsucc(T, x, 0) == 0); 11 | test(hhashput(T, x, 1)); 12 | test(hhashsucc(T, x, 1) == 1); 13 | test(hhashput(T, x, 1)); 14 | test(hhashsucc(T, x, 2) == 2); 15 | test(hhashput(T, x, 1)); 16 | test(hhashsucc(T, x, 3) == 3); 17 | test(!hhashput(T, x, 1)); 18 | --x; 19 | test(hhashput(T, x, 1)); 20 | test(hhashsucc(T, x, 0) == 0); 21 | test(!hhashput(T, x, 1)); 22 | hhashfree(T); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /test/test4.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test.h" 4 | 5 | int main(void) { 6 | ulong n = 16; 7 | HHash *T = hhashnew(n); 8 | ulong x = 15; 9 | test(hhashput(T, x, 1)); 10 | test(hhashsucc(T, x, 0) == 0); 11 | test(hhashput(T, x, 1)); 12 | test(hhashsucc(T, x, 1) == 1); 13 | test(hhashput(T, x, 1)); 14 | test(hhashsucc(T, x, 2) == 2); 15 | test(T->m == 3); 16 | x = 14; 17 | test(hhashput(T, x, 1)); 18 | test(hhashsucc(T, x, 0) == 0); 19 | test(hhashput(T, x, 1)); 20 | test(hhashsucc(T, x, 1) == 1); 21 | test(!hhashput(T, x, 1)); 22 | hhashfree(T); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /test/test5.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test.h" 4 | 5 | int find(HHash *T, ulong x) { 6 | ulong h = x % T->n; 7 | int i = hhashsucc(T, h, 0); 8 | while (i >= 0) { 9 | ulong y = hhashget(T, h, i); 10 | if (x == y) return 1; 11 | i = hhashsucc(T, h, i + 1); 12 | } 13 | return 0; 14 | } 15 | 16 | int main(void) { 17 | ulong n = 256; 18 | HHash *T = hhashnew(n); 19 | test(hhashput(T, 0x04, 0x04)); 20 | test(hhashput(T, 0x09, 0x09)); 21 | test(hhashput(T, 0x11, 0x11)); 22 | test(hhashput(T, 0x24, 0x24)); 23 | test(hhashput(T, 0x25, 0x25)); 24 | test(hhashput(T, 0x32, 0x32)); 25 | test(hhashput(T, 0x39, 0x39)); 26 | test(hhashput(T, 0x3c, 0x3c)); 27 | test(hhashput(T, 0x40, 0x40)); 28 | test(hhashput(T, 0x4b, 0x4b)); 29 | test(hhashput(T, 0x56, 0x56)); 30 | test(hhashput(T, 0x67, 0x67)); 31 | test(hhashput(T, 0x71, 0x71)); 32 | test(find(T, 0x04)); 33 | test(find(T, 0x09)); 34 | test(find(T, 0x11)); 35 | test(find(T, 0x24)); 36 | test(find(T, 0x25)); 37 | test(find(T, 0x32)); 38 | test(find(T, 0x39)); 39 | test(find(T, 0x3c)); 40 | test(find(T, 0x40)); 41 | test(find(T, 0x4b)); 42 | test(find(T, 0x56)); 43 | test(find(T, 0x67)); 44 | test(find(T, 0x71)); 45 | hhashfree(T); 46 | return 0; 47 | } 48 | -------------------------------------------------------------------------------- /test/test6.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "test.h" 4 | 5 | ulong fill(HHash *T) { 6 | ulong m = 0; 7 | for (ulong i = 0; i < 100; ++i) { 8 | ulong x = rand() % T->n; 9 | if (hhashsucc(T, x, 0) < 0) 10 | if (hhashput(T, x, x)) ++m; 11 | } 12 | return m; 13 | } 14 | 15 | ulong count(HHash *T) { 16 | ulong m = 0; 17 | ulong h = 0; 18 | while (h < T->n) { 19 | int i = hhashsucc(T, h, 0); 20 | while (i >= 0) { 21 | ++m; 22 | i = hhashsucc(T, h, i + 1); 23 | } 24 | ++h; 25 | } 26 | return m; 27 | } 28 | 29 | int main(void) { 30 | ulong n = 256; 31 | HHash *T = hhashnew(n); 32 | ulong m = fill(T); 33 | test(m == T->m); 34 | test(m == count(T)); 35 | hhashfree(T); 36 | return 0; 37 | } 38 | --------------------------------------------------------------------------------