1*134e1779SJakub Wojciech Klama /*
2*134e1779SJakub Wojciech Klama * Copyright 2016 Jakub Klama <jceel@FreeBSD.org>
3*134e1779SJakub Wojciech Klama * All rights reserved
4*134e1779SJakub Wojciech Klama *
5*134e1779SJakub Wojciech Klama * Redistribution and use in source and binary forms, with or without
6*134e1779SJakub Wojciech Klama * modification, are permitted providing that the following conditions
7*134e1779SJakub Wojciech Klama * are met:
8*134e1779SJakub Wojciech Klama * 1. Redistributions of source code must retain the above copyright
9*134e1779SJakub Wojciech Klama * notice, this list of conditions and the following disclaimer.
10*134e1779SJakub Wojciech Klama * 2. Redistributions in binary form must reproduce the above copyright
11*134e1779SJakub Wojciech Klama * notice, this list of conditions and the following disclaimer in the
12*134e1779SJakub Wojciech Klama * documentation and/or other materials provided with the distribution.
13*134e1779SJakub Wojciech Klama *
14*134e1779SJakub Wojciech Klama * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
15*134e1779SJakub Wojciech Klama * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16*134e1779SJakub Wojciech Klama * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17*134e1779SJakub Wojciech Klama * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
18*134e1779SJakub Wojciech Klama * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19*134e1779SJakub Wojciech Klama * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20*134e1779SJakub Wojciech Klama * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21*134e1779SJakub Wojciech Klama * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
22*134e1779SJakub Wojciech Klama * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
23*134e1779SJakub Wojciech Klama * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
24*134e1779SJakub Wojciech Klama * POSSIBILITY OF SUCH DAMAGE.
25*134e1779SJakub Wojciech Klama *
26*134e1779SJakub Wojciech Klama */
27*134e1779SJakub Wojciech Klama
28*134e1779SJakub Wojciech Klama #include <stdlib.h>
29*134e1779SJakub Wojciech Klama #include <string.h>
30*134e1779SJakub Wojciech Klama #include <errno.h>
31*134e1779SJakub Wojciech Klama #include <assert.h>
32*134e1779SJakub Wojciech Klama #include <pthread.h>
33*134e1779SJakub Wojciech Klama #include <sys/types.h>
34*134e1779SJakub Wojciech Klama #include <sys/queue.h>
35*134e1779SJakub Wojciech Klama #include "lib9p_impl.h"
36*134e1779SJakub Wojciech Klama #include "hashtable.h"
37*134e1779SJakub Wojciech Klama
38*134e1779SJakub Wojciech Klama static struct ht_item *ht_iter_advance(struct ht_iter *, struct ht_item *);
39*134e1779SJakub Wojciech Klama
40*134e1779SJakub Wojciech Klama void
ht_init(struct ht * h,ssize_t size)41*134e1779SJakub Wojciech Klama ht_init(struct ht *h, ssize_t size)
42*134e1779SJakub Wojciech Klama {
43*134e1779SJakub Wojciech Klama ssize_t i;
44*134e1779SJakub Wojciech Klama
45*134e1779SJakub Wojciech Klama memset(h, 0, sizeof(struct ht));
46*134e1779SJakub Wojciech Klama h->ht_nentries = size;
47*134e1779SJakub Wojciech Klama h->ht_entries = l9p_calloc((size_t)size, sizeof(struct ht_entry));
48*134e1779SJakub Wojciech Klama pthread_rwlock_init(&h->ht_rwlock, NULL);
49*134e1779SJakub Wojciech Klama
50*134e1779SJakub Wojciech Klama for (i = 0; i < size; i++)
51*134e1779SJakub Wojciech Klama TAILQ_INIT(&h->ht_entries[i].hte_items);
52*134e1779SJakub Wojciech Klama }
53*134e1779SJakub Wojciech Klama
54*134e1779SJakub Wojciech Klama void
ht_destroy(struct ht * h)55*134e1779SJakub Wojciech Klama ht_destroy(struct ht *h)
56*134e1779SJakub Wojciech Klama {
57*134e1779SJakub Wojciech Klama struct ht_entry *he;
58*134e1779SJakub Wojciech Klama struct ht_item *item, *tmp;
59*134e1779SJakub Wojciech Klama ssize_t i;
60*134e1779SJakub Wojciech Klama
61*134e1779SJakub Wojciech Klama for (i = 0; i < h->ht_nentries; i++) {
62*134e1779SJakub Wojciech Klama he = &h->ht_entries[i];
63*134e1779SJakub Wojciech Klama TAILQ_FOREACH_SAFE(item, &he->hte_items, hti_link, tmp) {
64*134e1779SJakub Wojciech Klama free(item);
65*134e1779SJakub Wojciech Klama }
66*134e1779SJakub Wojciech Klama }
67*134e1779SJakub Wojciech Klama
68*134e1779SJakub Wojciech Klama pthread_rwlock_destroy(&h->ht_rwlock);
69*134e1779SJakub Wojciech Klama free(h->ht_entries);
70*134e1779SJakub Wojciech Klama h->ht_entries = NULL;
71*134e1779SJakub Wojciech Klama }
72*134e1779SJakub Wojciech Klama
73*134e1779SJakub Wojciech Klama void *
ht_find(struct ht * h,uint32_t hash)74*134e1779SJakub Wojciech Klama ht_find(struct ht *h, uint32_t hash)
75*134e1779SJakub Wojciech Klama {
76*134e1779SJakub Wojciech Klama void *result;
77*134e1779SJakub Wojciech Klama
78*134e1779SJakub Wojciech Klama ht_rdlock(h);
79*134e1779SJakub Wojciech Klama result = ht_find_locked(h, hash);
80*134e1779SJakub Wojciech Klama ht_unlock(h);
81*134e1779SJakub Wojciech Klama return (result);
82*134e1779SJakub Wojciech Klama }
83*134e1779SJakub Wojciech Klama
84*134e1779SJakub Wojciech Klama void *
ht_find_locked(struct ht * h,uint32_t hash)85*134e1779SJakub Wojciech Klama ht_find_locked(struct ht *h, uint32_t hash)
86*134e1779SJakub Wojciech Klama {
87*134e1779SJakub Wojciech Klama struct ht_entry *entry;
88*134e1779SJakub Wojciech Klama struct ht_item *item;
89*134e1779SJakub Wojciech Klama
90*134e1779SJakub Wojciech Klama entry = &h->ht_entries[hash % h->ht_nentries];
91*134e1779SJakub Wojciech Klama
92*134e1779SJakub Wojciech Klama TAILQ_FOREACH(item, &entry->hte_items, hti_link) {
93*134e1779SJakub Wojciech Klama if (item->hti_hash == hash)
94*134e1779SJakub Wojciech Klama return (item->hti_data);
95*134e1779SJakub Wojciech Klama }
96*134e1779SJakub Wojciech Klama
97*134e1779SJakub Wojciech Klama return (NULL);
98*134e1779SJakub Wojciech Klama }
99*134e1779SJakub Wojciech Klama
100*134e1779SJakub Wojciech Klama int
ht_add(struct ht * h,uint32_t hash,void * value)101*134e1779SJakub Wojciech Klama ht_add(struct ht *h, uint32_t hash, void *value)
102*134e1779SJakub Wojciech Klama {
103*134e1779SJakub Wojciech Klama struct ht_entry *entry;
104*134e1779SJakub Wojciech Klama struct ht_item *item;
105*134e1779SJakub Wojciech Klama
106*134e1779SJakub Wojciech Klama ht_wrlock(h);
107*134e1779SJakub Wojciech Klama entry = &h->ht_entries[hash % h->ht_nentries];
108*134e1779SJakub Wojciech Klama
109*134e1779SJakub Wojciech Klama TAILQ_FOREACH(item, &entry->hte_items, hti_link) {
110*134e1779SJakub Wojciech Klama if (item->hti_hash == hash) {
111*134e1779SJakub Wojciech Klama errno = EEXIST;
112*134e1779SJakub Wojciech Klama ht_unlock(h);
113*134e1779SJakub Wojciech Klama return (-1);
114*134e1779SJakub Wojciech Klama }
115*134e1779SJakub Wojciech Klama }
116*134e1779SJakub Wojciech Klama
117*134e1779SJakub Wojciech Klama item = l9p_calloc(1, sizeof(struct ht_item));
118*134e1779SJakub Wojciech Klama item->hti_hash = hash;
119*134e1779SJakub Wojciech Klama item->hti_data = value;
120*134e1779SJakub Wojciech Klama TAILQ_INSERT_TAIL(&entry->hte_items, item, hti_link);
121*134e1779SJakub Wojciech Klama ht_unlock(h);
122*134e1779SJakub Wojciech Klama
123*134e1779SJakub Wojciech Klama return (0);
124*134e1779SJakub Wojciech Klama }
125*134e1779SJakub Wojciech Klama
126*134e1779SJakub Wojciech Klama int
ht_remove(struct ht * h,uint32_t hash)127*134e1779SJakub Wojciech Klama ht_remove(struct ht *h, uint32_t hash)
128*134e1779SJakub Wojciech Klama {
129*134e1779SJakub Wojciech Klama int result;
130*134e1779SJakub Wojciech Klama
131*134e1779SJakub Wojciech Klama ht_wrlock(h);
132*134e1779SJakub Wojciech Klama result = ht_remove_locked(h, hash);
133*134e1779SJakub Wojciech Klama ht_unlock(h);
134*134e1779SJakub Wojciech Klama return (result);
135*134e1779SJakub Wojciech Klama }
136*134e1779SJakub Wojciech Klama
137*134e1779SJakub Wojciech Klama int
ht_remove_locked(struct ht * h,uint32_t hash)138*134e1779SJakub Wojciech Klama ht_remove_locked(struct ht *h, uint32_t hash)
139*134e1779SJakub Wojciech Klama {
140*134e1779SJakub Wojciech Klama struct ht_entry *entry;
141*134e1779SJakub Wojciech Klama struct ht_item *item, *tmp;
142*134e1779SJakub Wojciech Klama ssize_t slot = hash % h->ht_nentries;
143*134e1779SJakub Wojciech Klama
144*134e1779SJakub Wojciech Klama entry = &h->ht_entries[slot];
145*134e1779SJakub Wojciech Klama
146*134e1779SJakub Wojciech Klama TAILQ_FOREACH_SAFE(item, &entry->hte_items, hti_link, tmp) {
147*134e1779SJakub Wojciech Klama if (item->hti_hash == hash) {
148*134e1779SJakub Wojciech Klama TAILQ_REMOVE(&entry->hte_items, item, hti_link);
149*134e1779SJakub Wojciech Klama free(item);
150*134e1779SJakub Wojciech Klama return (0);
151*134e1779SJakub Wojciech Klama }
152*134e1779SJakub Wojciech Klama }
153*134e1779SJakub Wojciech Klama
154*134e1779SJakub Wojciech Klama errno = ENOENT;
155*134e1779SJakub Wojciech Klama return (-1);
156*134e1779SJakub Wojciech Klama }
157*134e1779SJakub Wojciech Klama
158*134e1779SJakub Wojciech Klama /*
159*134e1779SJakub Wojciech Klama * Inner workings for advancing the iterator.
160*134e1779SJakub Wojciech Klama *
161*134e1779SJakub Wojciech Klama * If we have a current item, that tells us how to find the
162*134e1779SJakub Wojciech Klama * next item. If not, we get the first item from the next
163*134e1779SJakub Wojciech Klama * slot (well, the next slot with an item); in any case, we
164*134e1779SJakub Wojciech Klama * record the new slot and return the next item.
165*134e1779SJakub Wojciech Klama *
166*134e1779SJakub Wojciech Klama * For bootstrapping, iter->htit_slot can be -1 to start
167*134e1779SJakub Wojciech Klama * searching at slot 0.
168*134e1779SJakub Wojciech Klama *
169*134e1779SJakub Wojciech Klama * Caller must hold a lock on the table.
170*134e1779SJakub Wojciech Klama */
171*134e1779SJakub Wojciech Klama static struct ht_item *
ht_iter_advance(struct ht_iter * iter,struct ht_item * cur)172*134e1779SJakub Wojciech Klama ht_iter_advance(struct ht_iter *iter, struct ht_item *cur)
173*134e1779SJakub Wojciech Klama {
174*134e1779SJakub Wojciech Klama struct ht_item *next;
175*134e1779SJakub Wojciech Klama struct ht *h;
176*134e1779SJakub Wojciech Klama ssize_t slot;
177*134e1779SJakub Wojciech Klama
178*134e1779SJakub Wojciech Klama h = iter->htit_parent;
179*134e1779SJakub Wojciech Klama
180*134e1779SJakub Wojciech Klama if (cur == NULL)
181*134e1779SJakub Wojciech Klama next = NULL;
182*134e1779SJakub Wojciech Klama else
183*134e1779SJakub Wojciech Klama next = TAILQ_NEXT(cur, hti_link);
184*134e1779SJakub Wojciech Klama
185*134e1779SJakub Wojciech Klama if (next == NULL) {
186*134e1779SJakub Wojciech Klama slot = iter->htit_slot;
187*134e1779SJakub Wojciech Klama while (++slot < h->ht_nentries) {
188*134e1779SJakub Wojciech Klama next = TAILQ_FIRST(&h->ht_entries[slot].hte_items);
189*134e1779SJakub Wojciech Klama if (next != NULL)
190*134e1779SJakub Wojciech Klama break;
191*134e1779SJakub Wojciech Klama }
192*134e1779SJakub Wojciech Klama iter->htit_slot = slot;
193*134e1779SJakub Wojciech Klama }
194*134e1779SJakub Wojciech Klama return (next);
195*134e1779SJakub Wojciech Klama }
196*134e1779SJakub Wojciech Klama
197*134e1779SJakub Wojciech Klama /*
198*134e1779SJakub Wojciech Klama * Remove the current item - there must be one, or this is an
199*134e1779SJakub Wojciech Klama * error. This (necessarily) pre-locates the next item, so callers
200*134e1779SJakub Wojciech Klama * must not use it on an actively-changing table.
201*134e1779SJakub Wojciech Klama */
202*134e1779SJakub Wojciech Klama int
ht_remove_at_iter(struct ht_iter * iter)203*134e1779SJakub Wojciech Klama ht_remove_at_iter(struct ht_iter *iter)
204*134e1779SJakub Wojciech Klama {
205*134e1779SJakub Wojciech Klama struct ht_item *item;
206*134e1779SJakub Wojciech Klama struct ht *h;
207*134e1779SJakub Wojciech Klama ssize_t slot;
208*134e1779SJakub Wojciech Klama
209*134e1779SJakub Wojciech Klama assert(iter != NULL);
210*134e1779SJakub Wojciech Klama
211*134e1779SJakub Wojciech Klama if ((item = iter->htit_curr) == NULL) {
212*134e1779SJakub Wojciech Klama errno = EINVAL;
213*134e1779SJakub Wojciech Klama return (-1);
214*134e1779SJakub Wojciech Klama }
215*134e1779SJakub Wojciech Klama
216*134e1779SJakub Wojciech Klama /* remove the item from the table, saving the NEXT one */
217*134e1779SJakub Wojciech Klama h = iter->htit_parent;
218*134e1779SJakub Wojciech Klama ht_wrlock(h);
219*134e1779SJakub Wojciech Klama slot = iter->htit_slot;
220*134e1779SJakub Wojciech Klama iter->htit_next = ht_iter_advance(iter, item);
221*134e1779SJakub Wojciech Klama TAILQ_REMOVE(&h->ht_entries[slot].hte_items, item, hti_link);
222*134e1779SJakub Wojciech Klama ht_unlock(h);
223*134e1779SJakub Wojciech Klama
224*134e1779SJakub Wojciech Klama /* mark us as no longer on an item, then free it */
225*134e1779SJakub Wojciech Klama iter->htit_curr = NULL;
226*134e1779SJakub Wojciech Klama free(item);
227*134e1779SJakub Wojciech Klama
228*134e1779SJakub Wojciech Klama return (0);
229*134e1779SJakub Wojciech Klama }
230*134e1779SJakub Wojciech Klama
231*134e1779SJakub Wojciech Klama /*
232*134e1779SJakub Wojciech Klama * Initialize iterator. Subsequent ht_next calls will find the
233*134e1779SJakub Wojciech Klama * first item, then the next, and so on. Callers should in general
234*134e1779SJakub Wojciech Klama * not use this on actively-changing tables, though we do our best
235*134e1779SJakub Wojciech Klama * to make it semi-sensible.
236*134e1779SJakub Wojciech Klama */
237*134e1779SJakub Wojciech Klama void
ht_iter(struct ht * h,struct ht_iter * iter)238*134e1779SJakub Wojciech Klama ht_iter(struct ht *h, struct ht_iter *iter)
239*134e1779SJakub Wojciech Klama {
240*134e1779SJakub Wojciech Klama
241*134e1779SJakub Wojciech Klama iter->htit_parent = h;
242*134e1779SJakub Wojciech Klama iter->htit_curr = NULL;
243*134e1779SJakub Wojciech Klama iter->htit_next = NULL;
244*134e1779SJakub Wojciech Klama iter->htit_slot = -1; /* which will increment to 0 */
245*134e1779SJakub Wojciech Klama }
246*134e1779SJakub Wojciech Klama
247*134e1779SJakub Wojciech Klama /*
248*134e1779SJakub Wojciech Klama * Return the next item, which is the first item if we have not
249*134e1779SJakub Wojciech Klama * yet been called on this iterator, or the next item if we have.
250*134e1779SJakub Wojciech Klama */
251*134e1779SJakub Wojciech Klama void *
ht_next(struct ht_iter * iter)252*134e1779SJakub Wojciech Klama ht_next(struct ht_iter *iter)
253*134e1779SJakub Wojciech Klama {
254*134e1779SJakub Wojciech Klama struct ht_item *item;
255*134e1779SJakub Wojciech Klama struct ht *h;
256*134e1779SJakub Wojciech Klama
257*134e1779SJakub Wojciech Klama if ((item = iter->htit_next) == NULL) {
258*134e1779SJakub Wojciech Klama /* no pre-loaded next; find next from current */
259*134e1779SJakub Wojciech Klama h = iter->htit_parent;
260*134e1779SJakub Wojciech Klama ht_rdlock(h);
261*134e1779SJakub Wojciech Klama item = ht_iter_advance(iter, iter->htit_curr);
262*134e1779SJakub Wojciech Klama ht_unlock(h);
263*134e1779SJakub Wojciech Klama } else
264*134e1779SJakub Wojciech Klama iter->htit_next = NULL;
265*134e1779SJakub Wojciech Klama iter->htit_curr = item;
266*134e1779SJakub Wojciech Klama return (item == NULL ? NULL : item->hti_data);
267*134e1779SJakub Wojciech Klama }
268