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