]> code.delx.au - pulseaudio/blob - src/pulsecore/database-simple.c
hashmap: Use pa_free_cb_t instead of pa_free2_cb_t
[pulseaudio] / src / pulsecore / database-simple.c
1 /***
2 This file is part of PulseAudio.
3
4 Copyright 2009 Nokia Corporation
5 Contact: Maemo Multimedia <multimedia@maemo.org>
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, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
20 USA.
21 ***/
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <errno.h>
28 #include <sys/types.h>
29 #include <unistd.h>
30 #include <stdio.h>
31
32 #include <pulse/xmalloc.h>
33 #include <pulsecore/core-util.h>
34 #include <pulsecore/log.h>
35 #include <pulsecore/core-error.h>
36 #include <pulsecore/hashmap.h>
37
38 #include "database.h"
39
40
41 typedef struct simple_data {
42 char *filename;
43 char *tmp_filename;
44 pa_hashmap *map;
45 pa_bool_t read_only;
46 } simple_data;
47
48 typedef struct entry {
49 pa_datum key;
50 pa_datum data;
51 } entry;
52
53 void pa_datum_free(pa_datum *d) {
54 pa_assert(d);
55
56 pa_xfree(d->data);
57 d->data = NULL;
58 d->size = 0;
59 }
60
61 static int compare_func(const void *a, const void *b) {
62 const pa_datum *aa, *bb;
63
64 aa = (const pa_datum*)a;
65 bb = (const pa_datum*)b;
66
67 if (aa->size != bb->size)
68 return aa->size > bb->size ? 1 : -1;
69
70 return memcmp(aa->data, bb->data, aa->size);
71 }
72
73 /* pa_idxset_string_hash_func modified for our use */
74 static unsigned hash_func(const void *p) {
75 const pa_datum *d;
76 unsigned hash = 0;
77 const char *c;
78 unsigned i;
79
80 d = (const pa_datum*)p;
81 c = d->data;
82
83 for (i = 0; i < d->size; i++) {
84 hash = 31 * hash + (unsigned) *c;
85 c++;
86 }
87
88 return hash;
89 }
90
91 static entry* new_entry(const pa_datum *key, const pa_datum *data) {
92 entry *e;
93
94 pa_assert(key);
95 pa_assert(data);
96
97 e = pa_xnew0(entry, 1);
98 e->key.data = key->size > 0 ? pa_xmemdup(key->data, key->size) : NULL;
99 e->key.size = key->size;
100 e->data.data = data->size > 0 ? pa_xmemdup(data->data, data->size) : NULL;
101 e->data.size = data->size;
102 return e;
103 }
104
105 static void free_entry(entry *e) {
106 if (e) {
107 if (e->key.data)
108 pa_xfree(e->key.data);
109 if (e->data.data)
110 pa_xfree(e->data.data);
111 pa_xfree(e);
112 }
113 }
114
115 static int read_uint(FILE *f, uint32_t *res) {
116 size_t items = 0;
117 uint8_t values[4];
118 uint32_t tmp;
119 int i;
120
121 items = fread(&values, sizeof(values), sizeof(uint8_t), f);
122
123 if (feof(f)) /* EOF */
124 return 0;
125
126 if (ferror(f))
127 return -1;
128
129 for (i = 0; i < 4; ++i) {
130 tmp = values[i];
131 *res += (tmp << (i*8));
132 }
133
134 return items;
135 }
136
137 static int read_data(FILE *f, void **data, ssize_t *length) {
138 size_t items = 0;
139 uint32_t data_len = 0;
140
141 pa_assert(f);
142
143 *data = NULL;
144 *length = 0;
145
146 if ((items = read_uint(f, &data_len)) <= 0)
147 return -1;
148
149 if (data_len > 0) {
150 *data = pa_xmalloc0(data_len);
151 items = fread(*data, data_len, 1, f);
152
153 if (feof(f)) /* EOF */
154 goto reset;
155
156 if (ferror(f))
157 goto reset;
158
159 *length = data_len;
160
161 } else { /* no data? */
162 return -1;
163 }
164
165 return 0;
166
167 reset:
168 pa_xfree(*data);
169 *data = NULL;
170 *length = 0;
171 return -1;
172 }
173
174 static int fill_data(simple_data *db, FILE *f) {
175 pa_datum key;
176 pa_datum data;
177 void *d = NULL;
178 ssize_t l = 0;
179 pa_bool_t append = FALSE;
180 enum { FIELD_KEY = 0, FIELD_DATA } field = FIELD_KEY;
181
182 pa_assert(db);
183 pa_assert(db->map);
184
185 errno = 0;
186
187 key.size = 0;
188 key.data = NULL;
189
190 while (!read_data(f, &d, &l)) {
191
192 switch (field) {
193 case FIELD_KEY:
194 key.data = d;
195 key.size = l;
196 field = FIELD_DATA;
197 break;
198 case FIELD_DATA:
199 data.data = d;
200 data.size = l;
201 append = TRUE;
202 break;
203 }
204
205 if (append) {
206 entry *e = pa_xnew0(entry, 1);
207 e->key.data = key.data;
208 e->key.size = key.size;
209 e->data.data = data.data;
210 e->data.size = data.size;
211 pa_hashmap_put(db->map, &e->key, e);
212 append = FALSE;
213 field = FIELD_KEY;
214 }
215 }
216
217 if (ferror(f)) {
218 pa_log_warn("read error. %s", pa_cstrerror(errno));
219 pa_database_clear((pa_database*)db);
220 }
221
222 if (field == FIELD_DATA && d)
223 pa_xfree(d);
224
225 return pa_hashmap_size(db->map);
226 }
227
228 pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
229 FILE *f;
230 char *path;
231 simple_data *db;
232
233 pa_assert(fn);
234
235 path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
236 errno = 0;
237
238 f = pa_fopen_cloexec(path, "r");
239
240 if (f || errno == ENOENT) { /* file not found is ok */
241 db = pa_xnew0(simple_data, 1);
242 db->map = pa_hashmap_new(hash_func, compare_func);
243 db->filename = pa_xstrdup(path);
244 db->tmp_filename = pa_sprintf_malloc(".%s.tmp", db->filename);
245 db->read_only = !for_write;
246
247 if (f) {
248 fill_data(db, f);
249 fclose(f);
250 }
251 } else {
252 if (errno == 0)
253 errno = EIO;
254 db = NULL;
255 }
256
257 pa_xfree(path);
258
259 return (pa_database*) db;
260 }
261
262 void pa_database_close(pa_database *database) {
263 simple_data *db = (simple_data*)database;
264 pa_assert(db);
265
266 pa_database_sync(database);
267 pa_xfree(db->filename);
268 pa_xfree(db->tmp_filename);
269 pa_hashmap_free(db->map, (pa_free_cb_t) free_entry);
270 pa_xfree(db);
271 }
272
273 pa_datum* pa_database_get(pa_database *database, const pa_datum *key, pa_datum* data) {
274 simple_data *db = (simple_data*)database;
275 entry *e;
276
277 pa_assert(db);
278 pa_assert(key);
279 pa_assert(data);
280
281 e = pa_hashmap_get(db->map, key);
282
283 if (!e)
284 return NULL;
285
286 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
287 data->size = e->data.size;
288
289 return data;
290 }
291
292 int pa_database_set(pa_database *database, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
293 simple_data *db = (simple_data*)database;
294 entry *e;
295 int ret = 0;
296
297 pa_assert(db);
298 pa_assert(key);
299 pa_assert(data);
300
301 if (db->read_only)
302 return -1;
303
304 e = new_entry(key, data);
305
306 if (pa_hashmap_put(db->map, &e->key, e) < 0) {
307 /* entry with same key exists in hashmap */
308 entry *r;
309 if (overwrite) {
310 r = pa_hashmap_remove(db->map, key);
311 pa_hashmap_put(db->map, &e->key, e);
312 } else {
313 /* won't overwrite, so clean new entry */
314 r = e;
315 ret = -1;
316 }
317
318 free_entry(r);
319 }
320
321 return ret;
322 }
323
324 int pa_database_unset(pa_database *database, const pa_datum *key) {
325 simple_data *db = (simple_data*)database;
326 entry *e;
327
328 pa_assert(db);
329 pa_assert(key);
330
331 e = pa_hashmap_remove(db->map, key);
332 if (!e)
333 return -1;
334
335 free_entry(e);
336
337 return 0;
338 }
339
340 int pa_database_clear(pa_database *database) {
341 simple_data *db = (simple_data*)database;
342 entry *e;
343
344 pa_assert(db);
345
346 while ((e = pa_hashmap_steal_first(db->map)))
347 free_entry(e);
348
349 return 0;
350 }
351
352 signed pa_database_size(pa_database *database) {
353 simple_data *db = (simple_data*)database;
354 pa_assert(db);
355
356 return (signed) pa_hashmap_size(db->map);
357 }
358
359 pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) {
360 simple_data *db = (simple_data*)database;
361 entry *e;
362
363 pa_assert(db);
364 pa_assert(key);
365
366 e = pa_hashmap_first(db->map);
367
368 if (!e)
369 return NULL;
370
371 key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
372 key->size = e->key.size;
373
374 if (data) {
375 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
376 data->size = e->data.size;
377 }
378
379 return key;
380 }
381
382 pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) {
383 simple_data *db = (simple_data*)database;
384 entry *e;
385 entry *search;
386 void *state;
387 pa_bool_t pick_now;
388
389 pa_assert(db);
390 pa_assert(next);
391
392 if (!key)
393 return pa_database_first(database, next, data);
394
395 search = pa_hashmap_get(db->map, key);
396
397 state = NULL;
398 pick_now = FALSE;
399
400 while ((e = pa_hashmap_iterate(db->map, &state, NULL))) {
401 if (pick_now)
402 break;
403
404 if (search == e)
405 pick_now = TRUE;
406 }
407
408 if (!pick_now || !e)
409 return NULL;
410
411 next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
412 next->size = e->key.size;
413
414 if (data) {
415 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
416 data->size = e->data.size;
417 }
418
419 return next;
420 }
421
422 static int write_uint(FILE *f, const uint32_t num) {
423 size_t items;
424 uint8_t values[4];
425 int i;
426 errno = 0;
427
428 for (i = 0; i < 4; i++)
429 values[i] = (num >> (i*8)) & 0xFF;
430
431 items = fwrite(&values, sizeof(values), sizeof(uint8_t), f);
432
433 if (ferror(f))
434 return -1;
435
436 return items;
437 }
438
439 static int write_data(FILE *f, void *data, const size_t length) {
440 size_t items;
441 uint32_t len;
442
443 len = length;
444 if ((items = write_uint(f, len)) <= 0)
445 return -1;
446
447 items = fwrite(data, length, 1, f);
448
449 if (ferror(f) || items != 1)
450 return -1;
451
452 return 0;
453 }
454
455 static int write_entry(FILE *f, const entry *e) {
456 pa_assert(f);
457 pa_assert(e);
458
459 if (write_data(f, e->key.data, e->key.size) < 0)
460 return -1;
461 if (write_data(f, e->data.data, e->data.size) < 0)
462 return -1;
463
464 return 0;
465 }
466
467 int pa_database_sync(pa_database *database) {
468 simple_data *db = (simple_data*)database;
469 FILE *f;
470 void *state;
471 entry *e;
472
473 pa_assert(db);
474
475 if (db->read_only)
476 return 0;
477
478 errno = 0;
479
480 f = pa_fopen_cloexec(db->tmp_filename, "w");
481
482 if (!f)
483 goto fail;
484
485 state = NULL;
486 while((e = pa_hashmap_iterate(db->map, &state, NULL))) {
487 if (write_entry(f, e) < 0) {
488 pa_log_warn("error while writing to file. %s", pa_cstrerror(errno));
489 goto fail;
490 }
491 }
492
493 fclose(f);
494 f = NULL;
495
496 if (rename(db->tmp_filename, db->filename) < 0) {
497 pa_log_warn("error while renaming file. %s", pa_cstrerror(errno));
498 goto fail;
499 }
500
501 return 0;
502
503 fail:
504 if (f)
505 fclose(f);
506 return -1;
507 }