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