1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2004-2008 Lennart Poettering
5 Copyright 2006 Pierre Ossman <ossman@cendio.se> for Cendio AB
6
7 PulseAudio is free software; you can redistribute it and/or modify
8 it under the terms of the GNU Lesser General Public License as
9 published by the Free Software Foundation; either version 2.1 of the
10 License, or (at your option) any later version.
11
12 PulseAudio is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28
29 #include <pulse/xmalloc.h>
30 #include <pulsecore/flist.h>
31 #include <pulsecore/macro.h>
32
33 #include "idxset.h"
34
35 #define NBUCKETS 127
36
37 struct idxset_entry {
38 uint32_t idx;
39 void *data;
40
41 struct idxset_entry *data_next, *data_previous;
42 struct idxset_entry *index_next, *index_previous;
43 struct idxset_entry *iterate_next, *iterate_previous;
44 };
45
46 struct pa_idxset {
47 pa_hash_func_t hash_func;
48 pa_compare_func_t compare_func;
49
50 uint32_t current_index;
51
52 struct idxset_entry *iterate_list_head, *iterate_list_tail;
53 unsigned n_entries;
54 };
55
56 #define BY_DATA(i) ((struct idxset_entry**) ((uint8_t*) (i) + PA_ALIGN(sizeof(pa_idxset))))
57 #define BY_INDEX(i) (BY_DATA(i) + NBUCKETS)
58
59 PA_STATIC_FLIST_DECLARE(entries, 0, pa_xfree);
60
pa_idxset_string_hash_func(const void * p)61 unsigned pa_idxset_string_hash_func(const void *p) {
62 unsigned hash = 0;
63 const char *c;
64
65 for (c = p; *c; c++)
66 hash = 31 * hash + (unsigned) *c;
67
68 return hash;
69 }
70
pa_idxset_string_compare_func(const void * a,const void * b)71 int pa_idxset_string_compare_func(const void *a, const void *b) {
72 return strcmp(a, b);
73 }
74
pa_idxset_trivial_hash_func(const void * p)75 unsigned pa_idxset_trivial_hash_func(const void *p) {
76 return PA_PTR_TO_UINT(p);
77 }
78
pa_idxset_trivial_compare_func(const void * a,const void * b)79 int pa_idxset_trivial_compare_func(const void *a, const void *b) {
80 return a < b ? -1 : (a > b ? 1 : 0);
81 }
82
pa_idxset_new(pa_hash_func_t hash_func,pa_compare_func_t compare_func)83 pa_idxset* pa_idxset_new(pa_hash_func_t hash_func, pa_compare_func_t compare_func) {
84 pa_idxset *s;
85
86 s = pa_xmalloc0(PA_ALIGN(sizeof(pa_idxset)) + NBUCKETS*2*sizeof(struct idxset_entry*));
87
88 s->hash_func = hash_func ? hash_func : pa_idxset_trivial_hash_func;
89 s->compare_func = compare_func ? compare_func : pa_idxset_trivial_compare_func;
90
91 s->current_index = 0;
92 s->n_entries = 0;
93 s->iterate_list_head = s->iterate_list_tail = NULL;
94
95 return s;
96 }
97
remove_entry(pa_idxset * s,struct idxset_entry * e)98 static void remove_entry(pa_idxset *s, struct idxset_entry *e) {
99 pa_assert(s);
100 pa_assert(e);
101
102 /* Remove from iteration linked list */
103 if (e->iterate_next)
104 e->iterate_next->iterate_previous = e->iterate_previous;
105 else
106 s->iterate_list_tail = e->iterate_previous;
107
108 if (e->iterate_previous)
109 e->iterate_previous->iterate_next = e->iterate_next;
110 else
111 s->iterate_list_head = e->iterate_next;
112
113 /* Remove from data hash table */
114 if (e->data_next)
115 e->data_next->data_previous = e->data_previous;
116
117 if (e->data_previous)
118 e->data_previous->data_next = e->data_next;
119 else {
120 unsigned hash = s->hash_func(e->data) % NBUCKETS;
121 BY_DATA(s)[hash] = e->data_next;
122 }
123
124 /* Remove from index hash table */
125 if (e->index_next)
126 e->index_next->index_previous = e->index_previous;
127
128 if (e->index_previous)
129 e->index_previous->index_next = e->index_next;
130 else
131 BY_INDEX(s)[e->idx % NBUCKETS] = e->index_next;
132
133 if (pa_flist_push(PA_STATIC_FLIST_GET(entries), e) < 0)
134 pa_xfree(e);
135
136 pa_assert(s->n_entries >= 1);
137 s->n_entries--;
138 }
139
pa_idxset_free(pa_idxset * s,pa_free_cb_t free_cb)140 void pa_idxset_free(pa_idxset *s, pa_free_cb_t free_cb) {
141 pa_assert(s);
142
143 pa_idxset_remove_all(s, free_cb);
144 pa_xfree(s);
145 }
146
data_scan(pa_idxset * s,unsigned hash,const void * p)147 static struct idxset_entry* data_scan(pa_idxset *s, unsigned hash, const void *p) {
148 struct idxset_entry *e;
149 pa_assert(s);
150 pa_assert(hash < NBUCKETS);
151 pa_assert(p);
152
153 for (e = BY_DATA(s)[hash]; e; e = e->data_next)
154 if (s->compare_func(e->data, p) == 0)
155 return e;
156
157 return NULL;
158 }
159
index_scan(pa_idxset * s,unsigned hash,uint32_t idx)160 static struct idxset_entry* index_scan(pa_idxset *s, unsigned hash, uint32_t idx) {
161 struct idxset_entry *e;
162 pa_assert(s);
163 pa_assert(hash < NBUCKETS);
164
165 for (e = BY_INDEX(s)[hash]; e; e = e->index_next)
166 if (e->idx == idx)
167 return e;
168
169 return NULL;
170 }
171
pa_idxset_put(pa_idxset * s,void * p,uint32_t * idx)172 int pa_idxset_put(pa_idxset*s, void *p, uint32_t *idx) {
173 unsigned hash;
174 struct idxset_entry *e;
175
176 pa_assert(s);
177
178 hash = s->hash_func(p) % NBUCKETS;
179
180 if ((e = data_scan(s, hash, p))) {
181 if (idx)
182 *idx = e->idx;
183
184 return -1;
185 }
186
187 if (!(e = pa_flist_pop(PA_STATIC_FLIST_GET(entries))))
188 e = pa_xnew(struct idxset_entry, 1);
189
190 e->data = p;
191 e->idx = s->current_index++;
192
193 /* Insert into data hash table */
194 e->data_next = BY_DATA(s)[hash];
195 e->data_previous = NULL;
196 if (BY_DATA(s)[hash])
197 BY_DATA(s)[hash]->data_previous = e;
198 BY_DATA(s)[hash] = e;
199
200 hash = e->idx % NBUCKETS;
201
202 /* Insert into index hash table */
203 e->index_next = BY_INDEX(s)[hash];
204 e->index_previous = NULL;
205 if (BY_INDEX(s)[hash])
206 BY_INDEX(s)[hash]->index_previous = e;
207 BY_INDEX(s)[hash] = e;
208
209 /* Insert into iteration list */
210 e->iterate_previous = s->iterate_list_tail;
211 e->iterate_next = NULL;
212 if (s->iterate_list_tail) {
213 pa_assert(s->iterate_list_head);
214 s->iterate_list_tail->iterate_next = e;
215 } else {
216 pa_assert(!s->iterate_list_head);
217 s->iterate_list_head = e;
218 }
219 s->iterate_list_tail = e;
220
221 s->n_entries++;
222 pa_assert(s->n_entries >= 1);
223
224 if (idx)
225 *idx = e->idx;
226
227 return 0;
228 }
229
pa_idxset_get_by_index(pa_idxset * s,uint32_t idx)230 void* pa_idxset_get_by_index(pa_idxset*s, uint32_t idx) {
231 unsigned hash;
232 struct idxset_entry *e;
233
234 pa_assert(s);
235
236 hash = idx % NBUCKETS;
237
238 if (!(e = index_scan(s, hash, idx)))
239 return NULL;
240
241 return e->data;
242 }
243
pa_idxset_get_by_data(pa_idxset * s,const void * p,uint32_t * idx)244 void* pa_idxset_get_by_data(pa_idxset*s, const void *p, uint32_t *idx) {
245 unsigned hash;
246 struct idxset_entry *e;
247
248 pa_assert(s);
249
250 hash = s->hash_func(p) % NBUCKETS;
251
252 if (!(e = data_scan(s, hash, p)))
253 return NULL;
254
255 if (idx)
256 *idx = e->idx;
257
258 return e->data;
259 }
260
pa_idxset_remove_by_index(pa_idxset * s,uint32_t idx)261 void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
262 struct idxset_entry *e;
263 unsigned hash;
264 void *data;
265
266 pa_assert(s);
267
268 hash = idx % NBUCKETS;
269
270 if (!(e = index_scan(s, hash, idx)))
271 return NULL;
272
273 data = e->data;
274 remove_entry(s, e);
275
276 return data;
277 }
278
pa_idxset_remove_by_data(pa_idxset * s,const void * data,uint32_t * idx)279 void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
280 struct idxset_entry *e;
281 unsigned hash;
282 void *r;
283
284 pa_assert(s);
285
286 hash = s->hash_func(data) % NBUCKETS;
287
288 if (!(e = data_scan(s, hash, data)))
289 return NULL;
290
291 r = e->data;
292
293 if (idx)
294 *idx = e->idx;
295
296 remove_entry(s, e);
297
298 return r;
299 }
300
pa_idxset_remove_all(pa_idxset * s,pa_free_cb_t free_cb)301 void pa_idxset_remove_all(pa_idxset *s, pa_free_cb_t free_cb) {
302 pa_assert(s);
303
304 while (s->iterate_list_head) {
305 void *data = s->iterate_list_head->data;
306
307 remove_entry(s, s->iterate_list_head);
308
309 if (free_cb)
310 free_cb(data);
311 }
312 }
313
pa_idxset_rrobin(pa_idxset * s,uint32_t * idx)314 void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
315 unsigned hash;
316 struct idxset_entry *e;
317
318 pa_assert(s);
319 pa_assert(idx);
320
321 hash = *idx % NBUCKETS;
322
323 e = index_scan(s, hash, *idx);
324
325 if (e && e->iterate_next)
326 e = e->iterate_next;
327 else
328 e = s->iterate_list_head;
329
330 if (!e)
331 return NULL;
332
333 *idx = e->idx;
334 return e->data;
335 }
336
pa_idxset_iterate(pa_idxset * s,void ** state,uint32_t * idx)337 void *pa_idxset_iterate(pa_idxset *s, void **state, uint32_t *idx) {
338 struct idxset_entry *e;
339
340 pa_assert(s);
341 pa_assert(state);
342
343 if (*state == (void*) -1)
344 goto at_end;
345
346 if ((!*state && !s->iterate_list_head))
347 goto at_end;
348
349 e = *state ? *state : s->iterate_list_head;
350
351 if (e->iterate_next)
352 *state = e->iterate_next;
353 else
354 *state = (void*) -1;
355
356 if (idx)
357 *idx = e->idx;
358
359 return e->data;
360
361 at_end:
362 *state = (void *) -1;
363
364 if (idx)
365 *idx = PA_IDXSET_INVALID;
366
367 return NULL;
368 }
369
pa_idxset_steal_first(pa_idxset * s,uint32_t * idx)370 void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx) {
371 void *data;
372
373 pa_assert(s);
374
375 if (!s->iterate_list_head)
376 return NULL;
377
378 data = s->iterate_list_head->data;
379
380 if (idx)
381 *idx = s->iterate_list_head->idx;
382
383 remove_entry(s, s->iterate_list_head);
384
385 return data;
386 }
387
pa_idxset_first(pa_idxset * s,uint32_t * idx)388 void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
389 pa_assert(s);
390
391 if (!s->iterate_list_head) {
392 if (idx)
393 *idx = PA_IDXSET_INVALID;
394 return NULL;
395 }
396
397 if (idx)
398 *idx = s->iterate_list_head->idx;
399
400 return s->iterate_list_head->data;
401 }
402
pa_idxset_next(pa_idxset * s,uint32_t * idx)403 void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
404 struct idxset_entry *e;
405 unsigned hash;
406
407 pa_assert(s);
408 pa_assert(idx);
409
410 if (*idx == PA_IDXSET_INVALID)
411 return NULL;
412
413 hash = *idx % NBUCKETS;
414
415 if ((e = index_scan(s, hash, *idx))) {
416
417 e = e->iterate_next;
418
419 if (e) {
420 *idx = e->idx;
421 return e->data;
422 } else {
423 *idx = PA_IDXSET_INVALID;
424 return NULL;
425 }
426
427 } else {
428
429 /* If the entry passed doesn't exist anymore we try to find
430 * the next following */
431
432 for ((*idx)++; *idx < s->current_index; (*idx)++) {
433
434 hash = *idx % NBUCKETS;
435
436 if ((e = index_scan(s, hash, *idx))) {
437 *idx = e->idx;
438 return e->data;
439 }
440 }
441
442 *idx = PA_IDXSET_INVALID;
443 return NULL;
444 }
445 }
446
pa_idxset_size(pa_idxset * s)447 unsigned pa_idxset_size(pa_idxset*s) {
448 pa_assert(s);
449
450 return s->n_entries;
451 }
452
pa_idxset_isempty(pa_idxset * s)453 bool pa_idxset_isempty(pa_idxset *s) {
454 pa_assert(s);
455
456 return s->n_entries == 0;
457 }
458
pa_idxset_copy(pa_idxset * s,pa_copy_func_t copy_func)459 pa_idxset *pa_idxset_copy(pa_idxset *s, pa_copy_func_t copy_func) {
460 pa_idxset *copy;
461 struct idxset_entry *i;
462
463 pa_assert(s);
464
465 copy = pa_idxset_new(s->hash_func, s->compare_func);
466
467 for (i = s->iterate_list_head; i; i = i->iterate_next)
468 pa_idxset_put(copy, copy_func ? copy_func(i->data) : i->data, NULL);
469
470 return copy;
471 }
472