build-sys: bump soname
[pulseaudio-mirror.git] / src / pulsecore / database-simple.c
blob1f4caf717f83f4006f43c4110042000621ee4b96
1 /***
2 This file is part of PulseAudio.
4 Copyright 2009 Nokia Corporation
5 Contact: Maemo Multimedia <multimedia@maemo.org>
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.
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.
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 ***/
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
27 #include <errno.h>
28 #include <sys/stat.h>
29 #include <sys/types.h>
30 #include <fcntl.h>
31 #include <unistd.h>
32 #include <stdio.h>
34 #include <pulse/xmalloc.h>
35 #include <pulsecore/core-util.h>
36 #include <pulsecore/log.h>
37 #include <pulsecore/core-error.h>
38 #include <pulsecore/hashmap.h>
40 #include "database.h"
43 typedef struct simple_data {
44 char *filename;
45 char *tmp_filename;
46 pa_hashmap *map;
47 pa_bool_t read_only;
48 } simple_data;
50 typedef struct entry {
51 pa_datum key;
52 pa_datum data;
53 } entry;
55 void pa_datum_free(pa_datum *d) {
56 pa_assert(d);
58 pa_xfree(d->data);
59 d->data = NULL;
60 d->size = 0;
63 static int compare_func(const void *a, const void *b) {
64 const pa_datum *aa, *bb;
66 aa = (const pa_datum*)a;
67 bb = (const pa_datum*)b;
69 if (aa->size != bb->size)
70 return aa->size > bb->size ? 1 : -1;
72 return memcmp(aa->data, bb->data, aa->size);
75 /* pa_idxset_string_hash_func modified for our use */
76 static unsigned hash_func(const void *p) {
77 const pa_datum *d;
78 unsigned hash = 0;
79 const char *c;
80 unsigned i;
82 d = (const pa_datum*)p;
83 c = d->data;
85 for (i = 0; i < d->size; i++) {
86 hash = 31 * hash + (unsigned) *c;
87 c++;
90 return hash;
93 static entry* new_entry(const pa_datum *key, const pa_datum *data) {
94 entry *e;
96 pa_assert(key);
97 pa_assert(data);
99 e = pa_xnew0(entry, 1);
100 e->key.data = key->size > 0 ? pa_xmemdup(key->data, key->size) : NULL;
101 e->key.size = key->size;
102 e->data.data = data->size > 0 ? pa_xmemdup(data->data, data->size) : NULL;
103 e->data.size = data->size;
104 return e;
107 static void free_entry(entry *e) {
108 if (e) {
109 if (e->key.data)
110 pa_xfree(e->key.data);
111 if (e->data.data)
112 pa_xfree(e->data.data);
113 pa_xfree(e);
117 static int read_uint(FILE *f, uint32_t *res) {
118 size_t items = 0;
119 uint8_t values[4];
120 uint32_t tmp;
121 int i;
123 items = fread(&values, sizeof(values), sizeof(uint8_t), f);
125 if (feof(f)) /* EOF */
126 return 0;
128 if (ferror(f))
129 return -1;
131 for (i = 0; i < 4; ++i) {
132 tmp = values[i];
133 *res += (tmp << (i*8));
136 return items;
139 static int read_data(FILE *f, void **data, ssize_t *length) {
140 size_t items = 0;
141 uint32_t data_len = 0;
143 pa_assert(f);
145 *data = NULL;
146 *length = 0;
148 if ((items = read_uint(f, &data_len)) <= 0)
149 return -1;
151 if (data_len > 0) {
152 *data = pa_xmalloc0(data_len);
153 items = fread(*data, data_len, 1, f);
155 if (feof(f)) /* EOF */
156 goto reset;
158 if (ferror(f))
159 goto reset;
161 *length = data_len;
163 } else { /* no data? */
164 return -1;
167 return 0;
169 reset:
170 pa_xfree(*data);
171 *data = NULL;
172 *length = 0;
173 return -1;
176 static int fill_data(simple_data *db, FILE *f) {
177 pa_datum key;
178 pa_datum data;
179 void *d = NULL;
180 ssize_t l = 0;
181 pa_bool_t append = FALSE;
182 enum { FIELD_KEY = 0, FIELD_DATA } field = FIELD_KEY;
184 pa_assert(db);
185 pa_assert(db->map);
187 errno = 0;
189 key.size = 0;
190 key.data = NULL;
192 while (!read_data(f, &d, &l)) {
194 switch (field) {
195 case FIELD_KEY:
196 key.data = d;
197 key.size = l;
198 field = FIELD_DATA;
199 break;
200 case FIELD_DATA:
201 data.data = d;
202 data.size = l;
203 append = TRUE;
204 break;
207 if (append) {
208 entry *e = pa_xnew0(entry, 1);
209 e->key.data = key.data;
210 e->key.size = key.size;
211 e->data.data = data.data;
212 e->data.size = data.size;
213 pa_hashmap_put(db->map, &e->key, e);
214 append = FALSE;
215 field = FIELD_KEY;
219 if (ferror(f)) {
220 pa_log_warn("read error. %s", pa_cstrerror(errno));
221 pa_database_clear((pa_database*)db);
224 if (field == FIELD_DATA && d)
225 pa_xfree(d);
227 return pa_hashmap_size(db->map);
230 pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
231 FILE *f;
232 char *path;
233 simple_data *db;
235 pa_assert(fn);
237 path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
238 errno = 0;
240 f = fopen(path, "r");
242 if (f || errno == ENOENT) { /* file not found is ok */
243 db = pa_xnew0(simple_data, 1);
244 db->map = pa_hashmap_new(hash_func, compare_func);
245 db->filename = pa_xstrdup(path);
246 db->tmp_filename = pa_sprintf_malloc(".%s.tmp", db->filename);
247 db->read_only = !for_write;
249 if (f) {
250 fill_data(db, f);
251 fclose(f);
253 } else {
254 if (errno == 0)
255 errno = EIO;
256 db = NULL;
259 pa_xfree(path);
261 return (pa_database*) db;
264 void pa_database_close(pa_database *database) {
265 simple_data *db = (simple_data*)database;
266 pa_assert(db);
268 pa_database_sync(database);
269 pa_database_clear(database);
270 pa_xfree(db->filename);
271 pa_xfree(db->tmp_filename);
272 pa_hashmap_free(db->map, NULL, NULL);
273 pa_xfree(db);
276 pa_datum* pa_database_get(pa_database *database, const pa_datum *key, pa_datum* data) {
277 simple_data *db = (simple_data*)database;
278 entry *e;
280 pa_assert(db);
281 pa_assert(key);
282 pa_assert(data);
284 e = pa_hashmap_get(db->map, key);
286 if (!e)
287 return NULL;
289 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
290 data->size = e->data.size;
292 return data;
295 int pa_database_set(pa_database *database, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
296 simple_data *db = (simple_data*)database;
297 entry *e;
298 int ret = 0;
300 pa_assert(db);
301 pa_assert(key);
302 pa_assert(data);
304 if (db->read_only)
305 return -1;
307 e = new_entry(key, data);
309 if (pa_hashmap_put(db->map, &e->key, e) < 0) {
310 /* entry with same key exists in hashmap */
311 entry *r;
312 if (overwrite) {
313 r = pa_hashmap_remove(db->map, key);
314 pa_hashmap_put(db->map, &e->key, e);
315 } else {
316 /* wont't overwrite, so clean new entry */
317 r = e;
318 ret = -1;
321 free_entry(r);
324 return ret;
327 int pa_database_unset(pa_database *database, const pa_datum *key) {
328 simple_data *db = (simple_data*)database;
329 entry *e;
331 pa_assert(db);
332 pa_assert(key);
334 e = pa_hashmap_remove(db->map, key);
335 if (!e)
336 return -1;
338 free_entry(e);
340 return 0;
343 int pa_database_clear(pa_database *database) {
344 simple_data *db = (simple_data*)database;
345 entry *e;
347 pa_assert(db);
349 while ((e = pa_hashmap_steal_first(db->map)))
350 free_entry(e);
352 return 0;
355 signed pa_database_size(pa_database *database) {
356 simple_data *db = (simple_data*)database;
357 pa_assert(db);
359 return (signed) pa_hashmap_size(db->map);
362 pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) {
363 simple_data *db = (simple_data*)database;
364 entry *e;
366 pa_assert(db);
367 pa_assert(key);
369 e = pa_hashmap_first(db->map);
371 if (!e)
372 return NULL;
374 key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
375 key->size = e->key.size;
377 if (data) {
378 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
379 data->size = e->data.size;
382 return key;
385 pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) {
386 simple_data *db = (simple_data*)database;
387 entry *e;
388 entry *search;
389 void *state;
390 pa_bool_t pick_now;
392 pa_assert(db);
393 pa_assert(next);
395 if (!key)
396 return pa_database_first(database, next, data);
398 search = pa_hashmap_get(db->map, key);
400 state = NULL;
401 pick_now = FALSE;
403 while ((e = pa_hashmap_iterate(db->map, &state, NULL))) {
404 if (pick_now)
405 break;
407 if (search == e)
408 pick_now = TRUE;
411 if (!pick_now || !e)
412 return NULL;
414 next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
415 next->size = e->key.size;
417 if (data) {
418 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
419 data->size = e->data.size;
422 return next;
425 static int write_uint(FILE *f, const uint32_t num) {
426 size_t items;
427 uint8_t values[4];
428 int i;
429 errno = 0;
431 for (i = 0; i < 4; i++)
432 values[i] = (num >> (i*8)) & 0xFF;
434 items = fwrite(&values, sizeof(values), sizeof(uint8_t), f);
436 if (ferror(f))
437 return -1;
439 return items;
442 static int write_data(FILE *f, void *data, const size_t length) {
443 size_t items;
444 uint32_t len;
446 len = length;
447 if ((items = write_uint(f, len)) <= 0)
448 return -1;
450 items = fwrite(data, length, 1, f);
452 if (ferror(f) || items != 1)
453 return -1;
455 return 0;
458 static int write_entry(FILE *f, const entry *e) {
459 pa_assert(f);
460 pa_assert(e);
462 if (write_data(f, e->key.data, e->key.size) < 0)
463 return -1;
464 if (write_data(f, e->data.data, e->data.size) < 0)
465 return -1;
467 return 0;
470 int pa_database_sync(pa_database *database) {
471 simple_data *db = (simple_data*)database;
472 FILE *f;
473 void *state;
474 entry *e;
476 pa_assert(db);
478 if (db->read_only)
479 return 0;
481 errno = 0;
483 f = fopen(db->tmp_filename, "w");
485 if (!f)
486 goto fail;
488 state = NULL;
489 while((e = pa_hashmap_iterate(db->map, &state, NULL))) {
490 if (write_entry(f, e) < 0) {
491 pa_log_warn("error while writing to file. %s", pa_cstrerror(errno));
492 goto fail;
496 fclose(f);
497 f = NULL;
499 if (rename(db->tmp_filename, db->filename) < 0) {
500 pa_log_warn("error while renaming file. %s", pa_cstrerror(errno));
501 goto fail;
504 return 0;
506 fail:
507 if (f)
508 fclose(f);
509 return -1;