]> code.delx.au - pulseaudio/blob - src/pulsecore/database-simple.c
Use pa_hashmap_remove_and_free() where appropriate
[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 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
326 pa_assert(db);
327 pa_assert(key);
328
329 return pa_hashmap_remove_and_free(db->map, key);
330 }
331
332 int pa_database_clear(pa_database *database) {
333 simple_data *db = (simple_data*)database;
334
335 pa_assert(db);
336
337 pa_hashmap_remove_all(db->map);
338
339 return 0;
340 }
341
342 signed pa_database_size(pa_database *database) {
343 simple_data *db = (simple_data*)database;
344 pa_assert(db);
345
346 return (signed) pa_hashmap_size(db->map);
347 }
348
349 pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) {
350 simple_data *db = (simple_data*)database;
351 entry *e;
352
353 pa_assert(db);
354 pa_assert(key);
355
356 e = pa_hashmap_first(db->map);
357
358 if (!e)
359 return NULL;
360
361 key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
362 key->size = e->key.size;
363
364 if (data) {
365 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
366 data->size = e->data.size;
367 }
368
369 return key;
370 }
371
372 pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) {
373 simple_data *db = (simple_data*)database;
374 entry *e;
375 entry *search;
376 void *state;
377 bool pick_now;
378
379 pa_assert(db);
380 pa_assert(next);
381
382 if (!key)
383 return pa_database_first(database, next, data);
384
385 search = pa_hashmap_get(db->map, key);
386
387 state = NULL;
388 pick_now = false;
389
390 while ((e = pa_hashmap_iterate(db->map, &state, NULL))) {
391 if (pick_now)
392 break;
393
394 if (search == e)
395 pick_now = true;
396 }
397
398 if (!pick_now || !e)
399 return NULL;
400
401 next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
402 next->size = e->key.size;
403
404 if (data) {
405 data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
406 data->size = e->data.size;
407 }
408
409 return next;
410 }
411
412 static int write_uint(FILE *f, const uint32_t num) {
413 size_t items;
414 uint8_t values[4];
415 int i;
416 errno = 0;
417
418 for (i = 0; i < 4; i++)
419 values[i] = (num >> (i*8)) & 0xFF;
420
421 items = fwrite(&values, sizeof(values), sizeof(uint8_t), f);
422
423 if (ferror(f))
424 return -1;
425
426 return items;
427 }
428
429 static int write_data(FILE *f, void *data, const size_t length) {
430 size_t items;
431 uint32_t len;
432
433 len = length;
434 if ((items = write_uint(f, len)) <= 0)
435 return -1;
436
437 items = fwrite(data, length, 1, f);
438
439 if (ferror(f) || items != 1)
440 return -1;
441
442 return 0;
443 }
444
445 static int write_entry(FILE *f, const entry *e) {
446 pa_assert(f);
447 pa_assert(e);
448
449 if (write_data(f, e->key.data, e->key.size) < 0)
450 return -1;
451 if (write_data(f, e->data.data, e->data.size) < 0)
452 return -1;
453
454 return 0;
455 }
456
457 int pa_database_sync(pa_database *database) {
458 simple_data *db = (simple_data*)database;
459 FILE *f;
460 void *state;
461 entry *e;
462
463 pa_assert(db);
464
465 if (db->read_only)
466 return 0;
467
468 errno = 0;
469
470 f = pa_fopen_cloexec(db->tmp_filename, "w");
471
472 if (!f)
473 goto fail;
474
475 state = NULL;
476 while((e = pa_hashmap_iterate(db->map, &state, NULL))) {
477 if (write_entry(f, e) < 0) {
478 pa_log_warn("error while writing to file. %s", pa_cstrerror(errno));
479 goto fail;
480 }
481 }
482
483 fclose(f);
484 f = NULL;
485
486 if (rename(db->tmp_filename, db->filename) < 0) {
487 pa_log_warn("error while renaming file. %s", pa_cstrerror(errno));
488 goto fail;
489 }
490
491 return 0;
492
493 fail:
494 if (f)
495 fclose(f);
496 return -1;
497 }