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