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_contains(pa_idxset * s,const void * p)261 bool pa_idxset_contains(pa_idxset *s, const void *p) {
262 unsigned hash;
263 struct idxset_entry *e;
264
265 pa_assert(s);
266
267 hash = s->hash_func(p) % NBUCKETS;
268
269 if (!(e = data_scan(s, hash, p)))
270 return false;
271
272 return e->data == p;
273 }
274
pa_idxset_remove_by_index(pa_idxset * s,uint32_t idx)275 void* pa_idxset_remove_by_index(pa_idxset*s, uint32_t idx) {
276 struct idxset_entry *e;
277 unsigned hash;
278 void *data;
279
280 pa_assert(s);
281
282 hash = idx % NBUCKETS;
283
284 if (!(e = index_scan(s, hash, idx)))
285 return NULL;
286
287 data = e->data;
288 remove_entry(s, e);
289
290 return data;
291 }
292
pa_idxset_remove_by_data(pa_idxset * s,const void * data,uint32_t * idx)293 void* pa_idxset_remove_by_data(pa_idxset*s, const void *data, uint32_t *idx) {
294 struct idxset_entry *e;
295 unsigned hash;
296 void *r;
297
298 pa_assert(s);
299
300 hash = s->hash_func(data) % NBUCKETS;
301
302 if (!(e = data_scan(s, hash, data)))
303 return NULL;
304
305 r = e->data;
306
307 if (idx)
308 *idx = e->idx;
309
310 remove_entry(s, e);
311
312 return r;
313 }
314
pa_idxset_remove_all(pa_idxset * s,pa_free_cb_t free_cb)315 void pa_idxset_remove_all(pa_idxset *s, pa_free_cb_t free_cb) {
316 pa_assert(s);
317
318 while (s->iterate_list_head) {
319 void *data = s->iterate_list_head->data;
320
321 remove_entry(s, s->iterate_list_head);
322
323 if (free_cb)
324 free_cb(data);
325 }
326 }
327
pa_idxset_rrobin(pa_idxset * s,uint32_t * idx)328 void* pa_idxset_rrobin(pa_idxset *s, uint32_t *idx) {
329 unsigned hash;
330 struct idxset_entry *e;
331
332 pa_assert(s);
333 pa_assert(idx);
334
335 hash = *idx % NBUCKETS;
336
337 e = index_scan(s, hash, *idx);
338
339 if (e && e->iterate_next)
340 e = e->iterate_next;
341 else
342 e = s->iterate_list_head;
343
344 if (!e)
345 return NULL;
346
347 *idx = e->idx;
348 return e->data;
349 }
350
pa_idxset_iterate(pa_idxset * s,void ** state,uint32_t * idx)351 void *pa_idxset_iterate(pa_idxset *s, void **state, uint32_t *idx) {
352 struct idxset_entry *e;
353
354 pa_assert(s);
355 pa_assert(state);
356
357 if (*state == (void*) -1)
358 goto at_end;
359
360 if ((!*state && !s->iterate_list_head))
361 goto at_end;
362
363 e = *state ? *state : s->iterate_list_head;
364
365 if (e->iterate_next)
366 *state = e->iterate_next;
367 else
368 *state = (void*) -1;
369
370 if (idx)
371 *idx = e->idx;
372
373 return e->data;
374
375 at_end:
376 *state = (void *) -1;
377
378 if (idx)
379 *idx = PA_IDXSET_INVALID;
380
381 return NULL;
382 }
383
pa_idxset_reverse_iterate(pa_idxset * s,void ** state,uint32_t * idx)384 void *pa_idxset_reverse_iterate(pa_idxset *s, void **state, uint32_t *idx) {
385 struct idxset_entry *e;
386
387 pa_assert(s);
388 pa_assert(state);
389
390 if (*state == (void*) -1)
391 goto at_end;
392
393 if ((!*state && !s->iterate_list_tail))
394 goto at_end;
395
396 e = *state ? *state : s->iterate_list_tail;
397
398 if (e->iterate_previous)
399 *state = e->iterate_previous;
400 else
401 *state = (void*) -1;
402
403 if (idx)
404 *idx = e->idx;
405
406 return e->data;
407
408 at_end:
409 *state = (void *) -1;
410
411 if (idx)
412 *idx = PA_IDXSET_INVALID;
413
414 return NULL;
415 }
416
pa_idxset_steal_first(pa_idxset * s,uint32_t * idx)417 void* pa_idxset_steal_first(pa_idxset *s, uint32_t *idx) {
418 void *data;
419
420 pa_assert(s);
421
422 if (!s->iterate_list_head)
423 return NULL;
424
425 data = s->iterate_list_head->data;
426
427 if (idx)
428 *idx = s->iterate_list_head->idx;
429
430 remove_entry(s, s->iterate_list_head);
431
432 return data;
433 }
434
pa_idxset_steal_last(pa_idxset * s,uint32_t * idx)435 void* pa_idxset_steal_last(pa_idxset *s, uint32_t *idx) {
436 void *data;
437
438 pa_assert(s);
439
440 if (!s->iterate_list_tail)
441 return NULL;
442
443 data = s->iterate_list_tail->data;
444
445 if (idx)
446 *idx = s->iterate_list_tail->idx;
447
448 remove_entry(s, s->iterate_list_tail);
449
450 return data;
451 }
452
pa_idxset_first(pa_idxset * s,uint32_t * idx)453 void* pa_idxset_first(pa_idxset *s, uint32_t *idx) {
454 pa_assert(s);
455
456 if (!s->iterate_list_head) {
457 if (idx)
458 *idx = PA_IDXSET_INVALID;
459 return NULL;
460 }
461
462 if (idx)
463 *idx = s->iterate_list_head->idx;
464
465 return s->iterate_list_head->data;
466 }
467
pa_idxset_last(pa_idxset * s,uint32_t * idx)468 void* pa_idxset_last(pa_idxset *s, uint32_t *idx) {
469 pa_assert(s);
470
471 if (!s->iterate_list_tail) {
472 if (idx)
473 *idx = PA_IDXSET_INVALID;
474 return NULL;
475 }
476
477 if (idx)
478 *idx = s->iterate_list_tail->idx;
479
480 return s->iterate_list_tail->data;
481 }
482
pa_idxset_next(pa_idxset * s,uint32_t * idx)483 void *pa_idxset_next(pa_idxset *s, uint32_t *idx) {
484 struct idxset_entry *e;
485 unsigned hash;
486
487 pa_assert(s);
488 pa_assert(idx);
489
490 if (*idx == PA_IDXSET_INVALID)
491 return NULL;
492
493 hash = *idx % NBUCKETS;
494
495 if ((e = index_scan(s, hash, *idx))) {
496
497 e = e->iterate_next;
498
499 if (e) {
500 *idx = e->idx;
501 return e->data;
502 } else {
503 *idx = PA_IDXSET_INVALID;
504 return NULL;
505 }
506
507 } else {
508
509 /* If the entry passed doesn't exist anymore we try to find
510 * the next following */
511
512 for ((*idx)++; *idx < s->current_index; (*idx)++) {
513
514 hash = *idx % NBUCKETS;
515
516 if ((e = index_scan(s, hash, *idx))) {
517 *idx = e->idx;
518 return e->data;
519 }
520 }
521
522 *idx = PA_IDXSET_INVALID;
523 return NULL;
524 }
525 }
526
pa_idxset_previous(pa_idxset * s,uint32_t * idx)527 void *pa_idxset_previous(pa_idxset *s, uint32_t *idx) {
528 struct idxset_entry *e;
529 unsigned hash;
530
531 pa_assert(s);
532 pa_assert(idx);
533
534 if (*idx == PA_IDXSET_INVALID)
535 return NULL;
536
537 hash = *idx % NBUCKETS;
538
539 if ((e = index_scan(s, hash, *idx))) {
540
541 e = e->iterate_previous;
542
543 if (e) {
544 *idx = e->idx;
545 return e->data;
546 } else {
547 *idx = PA_IDXSET_INVALID;
548 return NULL;
549 }
550
551 } else {
552
553 /* If the entry passed doesn't exist anymore we try to find
554 * the preceding one. */
555
556 for ((*idx)--; *idx < s->current_index; (*idx)--) {
557
558 hash = *idx % NBUCKETS;
559
560 if ((e = index_scan(s, hash, *idx))) {
561 *idx = e->idx;
562 return e->data;
563 }
564 }
565
566 *idx = PA_IDXSET_INVALID;
567 return NULL;
568 }
569 }
570
pa_idxset_size(pa_idxset * s)571 unsigned pa_idxset_size(pa_idxset*s) {
572 pa_assert(s);
573
574 return s->n_entries;
575 }
576
pa_idxset_isempty(pa_idxset * s)577 bool pa_idxset_isempty(pa_idxset *s) {
578 pa_assert(s);
579
580 return s->n_entries == 0;
581 }
582
pa_idxset_isdisjoint(pa_idxset * s,pa_idxset * t)583 bool pa_idxset_isdisjoint(pa_idxset *s, pa_idxset *t) {
584 struct idxset_entry *i;
585
586 pa_assert(s);
587 pa_assert(t);
588
589 for (i = s->iterate_list_head; i; i = i->iterate_next)
590 if (pa_idxset_contains(t, i->data))
591 return false;
592
593 return true;
594 }
595
pa_idxset_issubset(pa_idxset * s,pa_idxset * t)596 bool pa_idxset_issubset(pa_idxset *s, pa_idxset *t) {
597 struct idxset_entry *i;
598
599 pa_assert(s);
600 pa_assert(t);
601
602 for (i = s->iterate_list_head; i; i = i->iterate_next)
603 if (!pa_idxset_contains(t, i->data))
604 return false;
605
606 return true;
607 }
608
pa_idxset_issuperset(pa_idxset * s,pa_idxset * t)609 bool pa_idxset_issuperset(pa_idxset *s, pa_idxset *t) {
610 return pa_idxset_issubset(t, s);
611 }
612
pa_idxset_equals(pa_idxset * s,pa_idxset * t)613 bool pa_idxset_equals(pa_idxset *s, pa_idxset *t) {
614 return pa_idxset_issubset(s, t) && pa_idxset_issuperset(s, t);
615 }
616
pa_idxset_copy(pa_idxset * s,pa_copy_func_t copy_func)617 pa_idxset *pa_idxset_copy(pa_idxset *s, pa_copy_func_t copy_func) {
618 pa_idxset *copy;
619 struct idxset_entry *i;
620
621 pa_assert(s);
622
623 copy = pa_idxset_new(s->hash_func, s->compare_func);
624
625 for (i = s->iterate_list_head; i; i = i->iterate_next)
626 pa_idxset_put(copy, copy_func ? copy_func(i->data) : i->data, NULL);
627
628 return copy;
629 }