mirror of
https://gitlab.freedesktop.org/pulseaudio/pulseaudio.git
synced 2025-10-29 05:40:23 -04:00
database: simple hashmap based database implementation
This commit is contained in:
parent
61105df13b
commit
ea5cdcbe52
3 changed files with 534 additions and 2 deletions
23
configure.ac
23
configure.ac
|
|
@ -626,10 +626,11 @@ AM_CONDITIONAL([HAVE_LIBSAMPLERATE], [test "x$HAVE_LIBSAMPLERATE" = x1])
|
||||||
|
|
||||||
HAVE_TDB=0
|
HAVE_TDB=0
|
||||||
HAVE_GDBM=0
|
HAVE_GDBM=0
|
||||||
|
HAVE_SIMPLEDB=0
|
||||||
|
|
||||||
AC_ARG_WITH(
|
AC_ARG_WITH(
|
||||||
[database],
|
[database],
|
||||||
AS_HELP_STRING([--with-database=auto|tdb|gdbm],[Choose database backend.]),[],[with_database=auto])
|
AS_HELP_STRING([--with-database=auto|tdb|gdbm|simple],[Choose database backend.]),[],[with_database=auto])
|
||||||
|
|
||||||
if test "x${with_database}" = "xauto" -o "x${with_database}" = "xtdb" ; then
|
if test "x${with_database}" = "xauto" -o "x${with_database}" = "xtdb" ; then
|
||||||
PKG_CHECK_MODULES(TDB, [ tdb ],
|
PKG_CHECK_MODULES(TDB, [ tdb ],
|
||||||
|
|
@ -659,7 +660,12 @@ if test "x${with_database}" = "xauto" -o "x${with_database}" = "xgdbm" ; then
|
||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if test "x${HAVE_TDB}" != x1 -a "x${HAVE_GDBM}" != x1; then
|
if test "x${with_database}" = "xauto" -o "x${with_database}" = "xsimple" ; then
|
||||||
|
HAVE_SIMPLEDB=1
|
||||||
|
with_database=simple
|
||||||
|
fi
|
||||||
|
|
||||||
|
if test "x${HAVE_TDB}" != x1 -a "x${HAVE_GDBM}" != x1 -a "x${HAVE_SIMPLEDB}" != x1; then
|
||||||
AC_MSG_ERROR([*** missing database backend])
|
AC_MSG_ERROR([*** missing database backend])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
|
@ -671,6 +677,10 @@ if test "x${HAVE_GDBM}" = x1 ; then
|
||||||
AC_DEFINE([HAVE_GDBM], 1, [Have gdbm?])
|
AC_DEFINE([HAVE_GDBM], 1, [Have gdbm?])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
if test "x${HAVE_SIMPLEDB}" = x1 ; then
|
||||||
|
AC_DEFINE([HAVE_SIMPLEDB], 1, [Have simple?])
|
||||||
|
fi
|
||||||
|
|
||||||
AC_SUBST(TDB_CFLAGS)
|
AC_SUBST(TDB_CFLAGS)
|
||||||
AC_SUBST(TDB_LIBS)
|
AC_SUBST(TDB_LIBS)
|
||||||
AC_SUBST(HAVE_TDB)
|
AC_SUBST(HAVE_TDB)
|
||||||
|
|
@ -681,6 +691,9 @@ AC_SUBST(GDBM_LIBS)
|
||||||
AC_SUBST(HAVE_GDBM)
|
AC_SUBST(HAVE_GDBM)
|
||||||
AM_CONDITIONAL([HAVE_GDBM], [test "x$HAVE_GDBM" = x1])
|
AM_CONDITIONAL([HAVE_GDBM], [test "x$HAVE_GDBM" = x1])
|
||||||
|
|
||||||
|
AC_SUBST(HAVE_SIMPLEDB)
|
||||||
|
AM_CONDITIONAL([HAVE_SIMPLEDB], [test "x$HAVE_SIMPLEDB" = x1])
|
||||||
|
|
||||||
#### OSS support (optional) ####
|
#### OSS support (optional) ####
|
||||||
|
|
||||||
AC_ARG_ENABLE([oss-output],
|
AC_ARG_ENABLE([oss-output],
|
||||||
|
|
@ -1491,6 +1504,11 @@ if test "x${HAVE_TDB}" = "x1" ; then
|
||||||
ENABLE_TDB=yes
|
ENABLE_TDB=yes
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
ENABLE_SIMPLEDB=no
|
||||||
|
if test "x${HAVE_SIMPLEDB}" = "x1" ; then
|
||||||
|
ENABLE_SIMPLEDB=yes
|
||||||
|
fi
|
||||||
|
|
||||||
ENABLE_OPENSSL=no
|
ENABLE_OPENSSL=no
|
||||||
if test "x${HAVE_OPENSSL}" = "x1" ; then
|
if test "x${HAVE_OPENSSL}" = "x1" ; then
|
||||||
ENABLE_OPENSSL=yes
|
ENABLE_OPENSSL=yes
|
||||||
|
|
@ -1540,6 +1558,7 @@ echo "
|
||||||
Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
|
Enable OpenSSL (for Airtunes): ${ENABLE_OPENSSL}
|
||||||
Enable tdb: ${ENABLE_TDB}
|
Enable tdb: ${ENABLE_TDB}
|
||||||
Enable gdbm: ${ENABLE_GDBM}
|
Enable gdbm: ${ENABLE_GDBM}
|
||||||
|
Enable simple database: ${ENABLE_SIMPLEDB}
|
||||||
|
|
||||||
System User: ${PA_SYSTEM_USER}
|
System User: ${PA_SYSTEM_USER}
|
||||||
System Group: ${PA_SYSTEM_GROUP}
|
System Group: ${PA_SYSTEM_GROUP}
|
||||||
|
|
|
||||||
|
|
@ -870,6 +870,9 @@ libpulsecore_@PA_MAJORMINORMICRO@_la_CFLAGS += $(TDB_CFLAGS)
|
||||||
libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(TDB_LIBS)
|
libpulsecore_@PA_MAJORMINORMICRO@_la_LIBADD += $(TDB_LIBS)
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
if HAVE_SIMPLEDB
|
||||||
|
libpulsecore_@PA_MAJORMINORMICRO@_la_SOURCES += pulsecore/database-simple.c
|
||||||
|
endif
|
||||||
|
|
||||||
# We split the foreign code off to not be annoyed by warnings we don't care about
|
# We split the foreign code off to not be annoyed by warnings we don't care about
|
||||||
noinst_LTLIBRARIES = libpulsecore-foreign.la
|
noinst_LTLIBRARIES = libpulsecore-foreign.la
|
||||||
|
|
|
||||||
510
src/pulsecore/database-simple.c
Normal file
510
src/pulsecore/database-simple.c
Normal file
|
|
@ -0,0 +1,510 @@
|
||||||
|
/***
|
||||||
|
This file is part of PulseAudio.
|
||||||
|
|
||||||
|
Copyright 2009 Nokia Corporation
|
||||||
|
Contact: Maemo Multimedia <multimedia@maemo.org>
|
||||||
|
|
||||||
|
PulseAudio is free software; you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Lesser General Public License as
|
||||||
|
published by the Free Software Foundation; either version 2.1 of the
|
||||||
|
License, or (at your option) any later version.
|
||||||
|
|
||||||
|
PulseAudio is distributed in the hope that it will be useful, but
|
||||||
|
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||||
|
Lesser General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Lesser General Public
|
||||||
|
License along with PulseAudio; if not, write to the Free Software
|
||||||
|
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
|
||||||
|
USA.
|
||||||
|
***/
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include <config.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <errno.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#include <pulse/xmalloc.h>
|
||||||
|
#include <pulsecore/core-util.h>
|
||||||
|
#include <pulsecore/log.h>
|
||||||
|
#include <pulsecore/core-error.h>
|
||||||
|
#include <pulsecore/hashmap.h>
|
||||||
|
|
||||||
|
#include "database.h"
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct simple_data {
|
||||||
|
char *filename;
|
||||||
|
char *tmp_filename;
|
||||||
|
pa_hashmap *map;
|
||||||
|
pa_bool_t read_only;
|
||||||
|
} simple_data;
|
||||||
|
|
||||||
|
typedef struct entry {
|
||||||
|
pa_datum key;
|
||||||
|
pa_datum data;
|
||||||
|
} entry;
|
||||||
|
|
||||||
|
void pa_datum_free(pa_datum *d) {
|
||||||
|
pa_assert(d);
|
||||||
|
|
||||||
|
pa_xfree(d->data);
|
||||||
|
d->data = NULL;
|
||||||
|
d->size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int compare_func(const void *a, const void *b) {
|
||||||
|
const pa_datum *aa, *bb;
|
||||||
|
|
||||||
|
aa = (const pa_datum*)a;
|
||||||
|
bb = (const pa_datum*)b;
|
||||||
|
|
||||||
|
if (aa->size != bb->size)
|
||||||
|
return aa->size > bb->size ? 1 : -1;
|
||||||
|
|
||||||
|
return memcmp(aa->data, bb->data, aa->size);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* pa_idxset_string_hash_func modified for our use */
|
||||||
|
static unsigned hash_func(const void *p) {
|
||||||
|
const pa_datum *d;
|
||||||
|
unsigned hash = 0;
|
||||||
|
const char *c;
|
||||||
|
unsigned i;
|
||||||
|
|
||||||
|
d = (const pa_datum*)p;
|
||||||
|
c = d->data;
|
||||||
|
|
||||||
|
for (i = 0; i < d->size; i++) {
|
||||||
|
hash = 31 * hash + (unsigned) *c;
|
||||||
|
c++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
static entry* new_entry(const pa_datum *key, const pa_datum *data) {
|
||||||
|
entry *e;
|
||||||
|
|
||||||
|
pa_assert(key);
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
e = pa_xnew0(entry, 1);
|
||||||
|
e->key.data = key->size > 0 ? pa_xmemdup(key->data, key->size) : NULL;
|
||||||
|
e->key.size = key->size;
|
||||||
|
e->data.data = data->size > 0 ? pa_xmemdup(data->data, data->size) : NULL;
|
||||||
|
e->data.size = data->size;
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void free_entry(entry *e) {
|
||||||
|
if (e) {
|
||||||
|
if (e->key.data)
|
||||||
|
pa_xfree(e->key.data);
|
||||||
|
if (e->data.data)
|
||||||
|
pa_xfree(e->data.data);
|
||||||
|
pa_xfree(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_uint(FILE *f, uint32_t *res) {
|
||||||
|
size_t items = 0;
|
||||||
|
uint8_t values[4];
|
||||||
|
uint32_t tmp;
|
||||||
|
int i;
|
||||||
|
|
||||||
|
items = fread(&values, sizeof(values), sizeof(uint8_t), f);
|
||||||
|
|
||||||
|
if (feof(f)) /* EOF */
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ferror(f))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; ++i) {
|
||||||
|
tmp = values[i];
|
||||||
|
*res += (tmp << (i*8));
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_data(FILE *f, void **data, ssize_t *length) {
|
||||||
|
size_t items = 0;
|
||||||
|
uint32_t data_len = 0;
|
||||||
|
|
||||||
|
pa_assert(f);
|
||||||
|
|
||||||
|
*data = NULL;
|
||||||
|
*length = 0;
|
||||||
|
|
||||||
|
if ((items = read_uint(f, &data_len)) <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
if (data_len > 0) {
|
||||||
|
*data = pa_xmalloc0(data_len);
|
||||||
|
items = fread(*data, data_len, 1, f);
|
||||||
|
|
||||||
|
if (feof(f)) /* EOF */
|
||||||
|
goto reset;
|
||||||
|
|
||||||
|
if (ferror(f))
|
||||||
|
goto reset;
|
||||||
|
|
||||||
|
*length = data_len;
|
||||||
|
|
||||||
|
} else { /* no data? */
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
reset:
|
||||||
|
pa_xfree(*data);
|
||||||
|
*data = NULL;
|
||||||
|
*length = 0;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int fill_data(simple_data *db, FILE *f) {
|
||||||
|
pa_datum key;
|
||||||
|
pa_datum data;
|
||||||
|
void *d = NULL;
|
||||||
|
ssize_t l = 0;
|
||||||
|
pa_bool_t append = FALSE;
|
||||||
|
enum { FIELD_KEY = 0, FIELD_DATA } field = FIELD_KEY;
|
||||||
|
|
||||||
|
pa_assert(db);
|
||||||
|
pa_assert(db->map);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
key.size = 0;
|
||||||
|
key.data = NULL;
|
||||||
|
|
||||||
|
while (!read_data(f, &d, &l)) {
|
||||||
|
|
||||||
|
switch (field) {
|
||||||
|
case FIELD_KEY:
|
||||||
|
key.data = d;
|
||||||
|
key.size = l;
|
||||||
|
field = FIELD_DATA;
|
||||||
|
break;
|
||||||
|
case FIELD_DATA:
|
||||||
|
data.data = d;
|
||||||
|
data.size = l;
|
||||||
|
append = TRUE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (append) {
|
||||||
|
entry *e = pa_xnew0(entry, 1);
|
||||||
|
e->key.data = key.data;
|
||||||
|
e->key.size = key.size;
|
||||||
|
e->data.data = data.data;
|
||||||
|
e->data.size = data.size;
|
||||||
|
pa_hashmap_put(db->map, &e->key, e);
|
||||||
|
append = FALSE;
|
||||||
|
field = FIELD_KEY;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ferror(f)) {
|
||||||
|
pa_log_warn("read error. %s", pa_cstrerror(errno));
|
||||||
|
pa_database_clear((pa_database*)db);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (field == FIELD_DATA && d)
|
||||||
|
pa_xfree(d);
|
||||||
|
|
||||||
|
return pa_hashmap_size(db->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_database* pa_database_open(const char *fn, pa_bool_t for_write) {
|
||||||
|
FILE *f;
|
||||||
|
char *path;
|
||||||
|
simple_data *db;
|
||||||
|
|
||||||
|
pa_assert(fn);
|
||||||
|
|
||||||
|
path = pa_sprintf_malloc("%s."CANONICAL_HOST".simple", fn);
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
f = fopen(path, "r");
|
||||||
|
|
||||||
|
if (f || errno == ENOENT) { /* file not found is ok */
|
||||||
|
db = pa_xnew0(simple_data, 1);
|
||||||
|
db->map = pa_hashmap_new(hash_func, compare_func);
|
||||||
|
db->filename = pa_xstrdup(path);
|
||||||
|
db->tmp_filename = pa_sprintf_malloc(".%s.tmp", db->filename);
|
||||||
|
db->read_only = !for_write;
|
||||||
|
|
||||||
|
if (f) {
|
||||||
|
fill_data(db, f);
|
||||||
|
fclose(f);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (errno == 0)
|
||||||
|
errno = EIO;
|
||||||
|
db = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_xfree(path);
|
||||||
|
|
||||||
|
return (pa_database*) db;
|
||||||
|
}
|
||||||
|
|
||||||
|
void pa_database_close(pa_database *database) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
pa_assert(db);
|
||||||
|
|
||||||
|
pa_database_sync(database);
|
||||||
|
pa_database_clear(database);
|
||||||
|
pa_xfree(db->filename);
|
||||||
|
pa_xfree(db->tmp_filename);
|
||||||
|
pa_hashmap_free(db->map, NULL, NULL);
|
||||||
|
pa_xfree(db);
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_datum* pa_database_get(pa_database *database, const pa_datum *key, pa_datum* data) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
entry *e;
|
||||||
|
|
||||||
|
pa_assert(db);
|
||||||
|
pa_assert(key);
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
e = pa_hashmap_get(db->map, key);
|
||||||
|
|
||||||
|
if (!e)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
|
||||||
|
data->size = e->data.size;
|
||||||
|
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pa_database_set(pa_database *database, const pa_datum *key, const pa_datum* data, pa_bool_t overwrite) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
entry *e;
|
||||||
|
int ret = 0;
|
||||||
|
|
||||||
|
pa_assert(db);
|
||||||
|
pa_assert(key);
|
||||||
|
pa_assert(data);
|
||||||
|
|
||||||
|
if (db->read_only)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
e = new_entry(key, data);
|
||||||
|
|
||||||
|
if (pa_hashmap_put(db->map, &e->key, e) < 0) {
|
||||||
|
/* entry with same key exists in hashmap */
|
||||||
|
entry *r;
|
||||||
|
if (overwrite) {
|
||||||
|
r = pa_hashmap_remove(db->map, key);
|
||||||
|
pa_hashmap_put(db->map, &e->key, e);
|
||||||
|
} else {
|
||||||
|
/* wont't overwrite, so clean new entry */
|
||||||
|
r = e;
|
||||||
|
ret = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
free_entry(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pa_database_unset(pa_database *database, const pa_datum *key) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
entry *e;
|
||||||
|
|
||||||
|
pa_assert(db);
|
||||||
|
pa_assert(key);
|
||||||
|
|
||||||
|
e = pa_hashmap_remove(db->map, key);
|
||||||
|
if (!e)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
free_entry(e);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pa_database_clear(pa_database *database) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
entry *e;
|
||||||
|
|
||||||
|
pa_assert(db);
|
||||||
|
|
||||||
|
while ((e = pa_hashmap_steal_first(db->map)))
|
||||||
|
free_entry(e);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
signed pa_database_size(pa_database *database) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
pa_assert(db);
|
||||||
|
|
||||||
|
return (signed) pa_hashmap_size(db->map);
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_datum* pa_database_first(pa_database *database, pa_datum *key, pa_datum *data) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
entry *e;
|
||||||
|
|
||||||
|
pa_assert(db);
|
||||||
|
pa_assert(key);
|
||||||
|
|
||||||
|
e = pa_hashmap_first(db->map);
|
||||||
|
|
||||||
|
if (!e)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
key->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
|
||||||
|
key->size = e->key.size;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
|
||||||
|
data->size = e->data.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
pa_datum* pa_database_next(pa_database *database, const pa_datum *key, pa_datum *next, pa_datum *data) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
entry *e;
|
||||||
|
entry *search;
|
||||||
|
void *state;
|
||||||
|
pa_bool_t pick_now;
|
||||||
|
|
||||||
|
pa_assert(db);
|
||||||
|
pa_assert(next);
|
||||||
|
|
||||||
|
if (!key)
|
||||||
|
return pa_database_first(database, next, data);
|
||||||
|
|
||||||
|
search = pa_hashmap_get(db->map, key);
|
||||||
|
|
||||||
|
state = NULL;
|
||||||
|
pick_now = FALSE;
|
||||||
|
|
||||||
|
while ((e = pa_hashmap_iterate(db->map, &state, NULL))) {
|
||||||
|
if (pick_now)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (search == e)
|
||||||
|
pick_now = TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!pick_now || !e)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
next->data = e->key.size > 0 ? pa_xmemdup(e->key.data, e->key.size) : NULL;
|
||||||
|
next->size = e->key.size;
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
data->data = e->data.size > 0 ? pa_xmemdup(e->data.data, e->data.size) : NULL;
|
||||||
|
data->size = e->data.size;
|
||||||
|
}
|
||||||
|
|
||||||
|
return next;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_uint(FILE *f, const uint32_t num) {
|
||||||
|
size_t items;
|
||||||
|
uint8_t values[4];
|
||||||
|
int i;
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
for (i = 0; i < 4; i++)
|
||||||
|
values[i] = (num >> (i*8)) & 0xFF;
|
||||||
|
|
||||||
|
items = fwrite(&values, sizeof(values), sizeof(uint8_t), f);
|
||||||
|
|
||||||
|
if (ferror(f))
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_data(FILE *f, void *data, const size_t length) {
|
||||||
|
size_t items;
|
||||||
|
uint32_t len;
|
||||||
|
|
||||||
|
len = length;
|
||||||
|
if ((items = write_uint(f, len)) <= 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
items = fwrite(data, length, 1, f);
|
||||||
|
|
||||||
|
if (ferror(f) || items != 1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int write_entry(FILE *f, const entry *e) {
|
||||||
|
pa_assert(f);
|
||||||
|
pa_assert(e);
|
||||||
|
|
||||||
|
if (write_data(f, e->key.data, e->key.size) < 0)
|
||||||
|
return -1;
|
||||||
|
if (write_data(f, e->data.data, e->data.size) < 0)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int pa_database_sync(pa_database *database) {
|
||||||
|
simple_data *db = (simple_data*)database;
|
||||||
|
FILE *f;
|
||||||
|
void *state;
|
||||||
|
entry *e;
|
||||||
|
|
||||||
|
pa_assert(db);
|
||||||
|
|
||||||
|
if (db->read_only)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
f = fopen(db->tmp_filename, "w");
|
||||||
|
|
||||||
|
if (!f)
|
||||||
|
goto fail;
|
||||||
|
|
||||||
|
state = NULL;
|
||||||
|
while((e = pa_hashmap_iterate(db->map, &state, NULL))) {
|
||||||
|
if (write_entry(f, e) < 0) {
|
||||||
|
pa_log_warn("error while writing to file. %s", pa_cstrerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fclose(f);
|
||||||
|
f = NULL;
|
||||||
|
|
||||||
|
if (rename(db->tmp_filename, db->filename) < 0) {
|
||||||
|
pa_log_warn("error while renaming file. %s", pa_cstrerror(errno));
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
if (f)
|
||||||
|
fclose(f);
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue