summaryrefslogtreecommitdiff
path: root/ctdb/common/db_hash.c
diff options
context:
space:
mode:
authorAmitay Isaacs <amitay@gmail.com>2015-03-17 12:35:31 +1100
committerAmitay Isaacs <amitay@samba.org>2015-10-07 14:53:28 +0200
commite5592f9fc018b4b22fbdba4f45526210f5f41692 (patch)
tree17fc78e19875b6b9ce126390d25e3ea9bb794b7a /ctdb/common/db_hash.c
parentacf5ebfa906b226d7db2051c9716dd9091d7ecb7 (diff)
downloadsamba-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.c268
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;
+}