diff options
author | Amitay Isaacs <amitay@gmail.com> | 2015-03-17 12:35:31 +1100 |
---|---|---|
committer | Amitay Isaacs <amitay@samba.org> | 2015-10-07 14:53:28 +0200 |
commit | e5592f9fc018b4b22fbdba4f45526210f5f41692 (patch) | |
tree | 17fc78e19875b6b9ce126390d25e3ea9bb794b7a /ctdb/common/db_hash.c | |
parent | acf5ebfa906b226d7db2051c9716dd9091d7ecb7 (diff) | |
download | samba-e5592f9fc018b4b22fbdba4f45526210f5f41692.tar.gz |
ctdb-common: Add db_hash abstraction
A hash table implemented using in-memory tdb backend.
Signed-off-by: Amitay Isaacs <amitay@gmail.com>
Reviewed-by: Martin Schwenke <martin@meltin.net>
Diffstat (limited to 'ctdb/common/db_hash.c')
-rw-r--r-- | ctdb/common/db_hash.c | 268 |
1 files changed, 268 insertions, 0 deletions
diff --git a/ctdb/common/db_hash.c b/ctdb/common/db_hash.c new file mode 100644 index 00000000000..6a23337e516 --- /dev/null +++ b/ctdb/common/db_hash.c @@ -0,0 +1,268 @@ +/* + Using tdb as a hash table + + Copyright (C) Amitay Isaacs 2015 + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program 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 General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see <http://www.gnu.org/licenses/>. +*/ + +#include "replace.h" +#include "system/filesys.h" + +#include <talloc.h> +#include <tdb.h> + +#include "common/db_hash.h" + +struct db_hash_context { + struct tdb_context *db; +}; + + +static int db_hash_destructor(struct db_hash_context *dh) +{ + if (dh->db != NULL) { + tdb_close(dh->db); + dh->db = NULL; + } + return 0; +} + +int db_hash_init(TALLOC_CTX *mem_ctx, const char *name, int hash_size, + enum db_hash_type type, struct db_hash_context **result) +{ + struct db_hash_context *dh; + int tdb_flags = TDB_INTERNAL | TDB_DISALLOW_NESTING; + + dh = talloc_zero(mem_ctx, struct db_hash_context); + if (dh == NULL) { + return ENOMEM; + } + + if (type == DB_HASH_COMPLEX) { + tdb_flags |= TDB_INCOMPATIBLE_HASH; + } + + dh->db = tdb_open(name, hash_size, tdb_flags, O_RDWR|O_CREAT, 0); + if (dh->db == NULL) { + talloc_free(dh); + return ENOMEM; + } + + talloc_set_destructor(dh, db_hash_destructor); + *result = dh; + return 0; +} + +static int db_hash_map_tdb_error(struct db_hash_context *dh) +{ + enum TDB_ERROR tdb_err; + int ret; + + tdb_err = tdb_error(dh->db); + switch (tdb_err) { + case TDB_SUCCESS: + ret = 0; break; + case TDB_ERR_OOM: + ret = ENOMEM; break; + case TDB_ERR_EXISTS: + ret = EEXIST; break; + case TDB_ERR_NOEXIST: + ret = ENOENT; break; + case TDB_ERR_EINVAL: + ret = EINVAL; break; + default: + ret = EIO; break; + } + return ret; +} + +int db_hash_insert(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen, + uint8_t *databuf, size_t datalen) +{ + TDB_DATA key, data; + int ret; + + if (dh == NULL) { + return EINVAL; + } + + key.dptr = keybuf; + key.dsize = keylen; + + data.dptr = databuf; + data.dsize = datalen; + + ret = tdb_store(dh->db, key, data, TDB_INSERT); + if (ret != 0) { + ret = db_hash_map_tdb_error(dh); + } + return ret; +} + +int db_hash_add(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen, + uint8_t *databuf, size_t datalen) +{ + TDB_DATA key, data; + int ret; + + if (dh == NULL) { + return EINVAL; + } + + key.dptr = keybuf; + key.dsize = keylen; + + data.dptr = databuf; + data.dsize = datalen; + + ret = tdb_store(dh->db, key, data, TDB_REPLACE); + if (ret != 0) { + ret = db_hash_map_tdb_error(dh); + } + return ret; +} + +int db_hash_delete(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen) +{ + TDB_DATA key; + int ret; + + key.dptr = keybuf; + key.dsize = keylen; + + if (dh == NULL) { + return EINVAL; + } + + ret = tdb_delete(dh->db, key); + if (ret != 0) { + ret = db_hash_map_tdb_error(dh); + } + return ret; +} + +struct db_hash_fetch_state { + db_hash_record_parser_fn parser; + void *private_data; +}; + +static int db_hash_fetch_parser(TDB_DATA key, TDB_DATA data, void *private_data) +{ + struct db_hash_fetch_state *state = + (struct db_hash_fetch_state *)private_data; + int ret; + + ret = state->parser(key.dptr, key.dsize, data.dptr, data.dsize, + state->private_data); + return ret; +} + +int db_hash_fetch(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen, + db_hash_record_parser_fn parser, void *private_data) +{ + struct db_hash_fetch_state state; + TDB_DATA key; + int ret; + + if (dh == NULL || parser == NULL) { + return EINVAL; + } + + state.parser = parser; + state.private_data = private_data; + + key.dptr = keybuf; + key.dsize = keylen; + + ret = tdb_parse_record(dh->db, key, db_hash_fetch_parser, &state); + if (ret == -1) { + return ENOENT; + } + return ret; +} + +int db_hash_exists(struct db_hash_context *dh, uint8_t *keybuf, size_t keylen) +{ + TDB_DATA key; + int ret; + + if (dh == NULL) { + return EINVAL; + } + + key.dptr = keybuf; + key.dsize = keylen; + + ret = tdb_exists(dh->db, key); + if (ret == 1) { + /* Key found */ + ret = 0; + } else { + ret = db_hash_map_tdb_error(dh); + if (ret == 0) { + ret = ENOENT; + } + } + return ret; +} + +struct db_hash_traverse_state { + db_hash_record_parser_fn parser; + void *private_data; +}; + +static int db_hash_traverse_parser(struct tdb_context *tdb, + TDB_DATA key, TDB_DATA data, + void *private_data) +{ + struct db_hash_traverse_state *state = + (struct db_hash_traverse_state *)private_data; + + return state->parser(key.dptr, key.dsize, data.dptr, data.dsize, + state->private_data); +} + +int db_hash_traverse(struct db_hash_context *dh, + db_hash_record_parser_fn parser, void *private_data, + int *count) +{ + struct db_hash_traverse_state state; + int ret; + + if (dh == NULL) { + return EINVAL; + } + + /* Special case, for counting records */ + if (parser == NULL) { + ret = tdb_traverse_read(dh->db, NULL, NULL); + } else { + state.parser = parser; + state.private_data = private_data; + + ret = tdb_traverse_read(dh->db, db_hash_traverse_parser, &state); + } + + if (ret == -1) { + ret = db_hash_map_tdb_error(dh); + } else { + if (count != NULL) { + *count = ret; + } + ret = 0; + } + + return ret; +} |