diff --git a/CMakeLists.txt b/CMakeLists.txt index 3c18deda471cd5aa2eb6246bb2a3b06ecd70edb3..ecaf5efeb98c0a8350b6ef22458a607f63885b8d 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ file(GLOB DAP_CHAIN_GLOBAL_DB_HDR *.h) add_library(${PROJECT_NAME} STATIC ${DAP_CHAIN_GLOBAL_DB_SRC} ${DAP_CHAIN_GLOBAL_DB_HDR}) -target_link_libraries(dap_chain_global_db dap_core dap_crypto dap_chain dap_chain_crypto ldb talloc tevent sqlite3) +target_link_libraries(dap_chain_global_db dap_core dap_crypto dap_chain dap_chain_crypto ldb talloc tevent sqlite3 ${CMAKE_CURRENT_SOURCE_DIR}/libcuttdb.a) target_include_directories(dap_chain_global_db INTERFACE .) set(${PROJECT_NAME}_DEFINITIONS CACHE INTERNAL "${PROJECT_NAME}: Definitions" FORCE) diff --git a/cuttdb.h b/cuttdb.h new file mode 100644 index 0000000000000000000000000000000000000000..e3eeac9c969d42ca70e72936e667f006e0810abc --- /dev/null +++ b/cuttdb.h @@ -0,0 +1,215 @@ +/* + * CuttDB - a fast key-value storage engine + * + * + * http://code.google.com/p/cuttdb/ + * + * Copyright (c) 2012, Siyuan Fu. All rights reserved. + * Use and distribution licensed under the BSD license. + * See the LICENSE file for full text + * + * Author: Siyuan Fu <fusiyuan2010@gmail.com> + * + */ + + +#ifndef _CUTTDB_H_ +#define _CUTTDB_H_ +#include <stdint.h> +#include <stdbool.h> + +#if defined(__cplusplus) +extern "C" { +#endif + +typedef struct CDB CDB; +typedef void (*CDB_ERRCALLBACK)(void *, int, const char *, int); +typedef bool (*CDB_ITERCALLBACK)(void *, const char *, int, const char *, int, uint32_t, uint64_t); + +/* performance statistical information of an database instance */ +typedef struct { + /* number of records in db */ + uint64_t rnum; + /* number of records in cache */ + uint64_t rcnum; + /* number of pages in db */ + uint64_t pnum; + /* number of pages in cache */ + uint64_t pcnum; + /* cache hit of record cache */ + uint64_t rchit; + /* cache miss of record cache */ + uint64_t rcmiss; + /* cache hit of page cache */ + uint64_t pchit; + /* cache miss of page cache */ + uint64_t pcmiss; + /* average disk read latency */ + uint32_t rlatcy; + /* average disk write latency */ + uint32_t wlatcy; +} CDBSTAT; + +/* options to open a database*/ +enum { + /* create an database if not exist */ + CDB_CREAT = 0x1, + /* clean the database if already exist */ + CDB_TRUNC = 0x2, + /* fill the cache when start up */ + CDB_PAGEWARMUP = 0x4, +}; + +/* error codes */ +enum { + CDB_SUCCESS = 0, + CDB_NOTFOUND, + CDB_EXIST, + CDB_DIRNOEXIST, + CDB_OPENERR, + CDB_PIDEXIST, + CDB_DATAERRDAT, + CDB_DATAERRIDX, + CDB_WRITEERR, + CDB_READERR, + CDB_NOFID, + CDB_INTERNALERR, + CDB_DATAERRMETA, + CDB_MEMDBNOCACHE, +}; + +/* record insertion options */ +enum { + CDB_OVERWRITE = 0, + CDB_INSERTIFEXIST = 0x1, + CDB_INSERTIFNOEXIST = 0x2, + CDB_INSERTCACHE = 0x8, +}; + +/* if database path is CDB_MEMDB, records are never written to disk, they stay in cache only */ +#define CDB_MEMDB ":memory:" + +/* + WARNING: + + the library has auxiliary thread, which means do fork() after open a database will cause + unpredictable situation. +*/ + +/* create an cuttdb object, which should be freed by cdb_destory() */ +CDB *cdb_new(); + +/* cdb_option() must be called before cdb_open() + + the second parameter 'hsize' indicates the size of main hash table, which can't be + modified after the database be created. To get better performance, it is suggest to + set the 'hsize' to 10% - 1% of the total number of records. The default value 1 million + should be proper for about 100 million records. Too large or small of the value would + lead to drop in speed or waste of memory + + the third parameter 'rcacheMB' indicates the size limit of record cache (measured by + MegaBytes), every record in cache would have about 40 bytes overhead. + + the fourth parameter 'pcacheMB' indicates the size limit of index page cache (measured + by MegaBytes). If a record is not in record cache, it will be read by only 1 disk seek + with enough page cache, or it have to make an extra disk seek to load the page. + cuttdb will use about {10 * number of records} bytes to cache all index pages, which + ensures fastest 'set' operation. + + the default parameter is (_db, 1000000, 128, 1024) + + return 0 if success, or -1 at failure. */ +int cdb_option(CDB *db, int hsize, int rcacheMB, int pcacheMB); + +/* Enable bloomfilter, size should be the estimated number of records in database + must be called before cdb_open(), + The value is 100000 at minimum. Memory cost of bloomfilter is size/8 bytes */ +void cdb_option_bloomfilter(CDB *db, uint64_t size); + +/* this is an advanced parameter. It is the size for cuttdb making a read from disk. + CuttDB do not know the record size even if the index is in memory, + so at least a read with default size will performed while in cdb_get(). + The value is recommended to be larger than the size of most records in database, + unless the records are mostly larger than tens of KB. + If the value is much larger than recommended, it will be a waste of computing. + The value can only be 65536 at maximum, 1024 at minimum */ +void cdb_option_areadsize(CDB *db, uint32_t size); + +/* open an database, 'file' should be an existing directory, or CDB_MEMDB for temporary store, + 'mode' should be combination of CDB_CREAT / CDB_TRUNC / CDB_PAGEWARMUP + CDB_PAGEWARMUP means to warm up page cache while opening + If there is a file called 'force_recovery' in the data directory, even if it might be made by 'touch force_recovery', + a force recovery will happen to rebuild the index (be aware that some deleted records would reappear after this) + */ +int cdb_open(CDB *db, const char *file, int mode); + + +/* simplified cdb_set2, insert a record with CDB_OVERWRITE and never expire */ +int cdb_set(CDB *db, const char *key, int ksize, const char *val, int vsize); + +/* set a record by 'key' and 'value', + opt could be bit combination of CDB_INSERTCACHE and one in {CDB_INSERTIFEXIST, CDB_INSERTNOEXIST, + CDB_OVERWRITE} + expire is the time for the record be valid, measured by second. 0 means never expire. + return 0 if success, or -1 at failure. */ +int cdb_set2(CDB *db, const char *key, int ksize, const char *val, int vsize, int opt, int expire); + + +/* get an record by 'key', the value will be allocated and passed out by 'val', its size is + 'vsize'. return 0 if success, or -1 at failure. */ +int cdb_get(CDB *db, const char *key, int ksize, void **val, int *vsize); + + +/* the val got by cdb_get should be freed by this for safety. + If there is more than one memory allocator */ +void cdb_free_val(void **val); + + +/* delete an record by 'key'. However ,the space of the record would not be recycled. + 'vsize'. return 0 if success, or -1 at failure. */ +int cdb_del(CDB *db, const char *key, int ksize); + + +/* create a new iterator begins at given operation id */ +void *cdb_iterate_new(CDB *db, uint64_t oid); + +/* iterate through the database with a callback, the function would stop if callback returned false + The callback should accept key, ksize, value, vsize, expire time, oid + Returns the number of records have been visited */ +uint64_t cdb_iterate(CDB *db, CDB_ITERCALLBACK itcb, void *arg, void *iter); + +/* destroy the iterator */ +void cdb_iterate_destroy(CDB *db, void *iter); + +/* get the current statistic information of db. 'stat' should be the struct already allocated. + if 'stat' is NULL, the statistic will be reset to zero. */ +void cdb_stat(CDB *db, CDBSTAT *stat); + + +/* close the database. IT MUST BE CALLED BEFORE PROGRAM EXITS TO ENSURE DATA COMPLETION */ +int cdb_close(CDB *db); + + +/* close the database if it opened, and free the object */ +int cdb_destroy(CDB *db); + + +/* get last error number in current thread */ +int cdb_errno(CDB *db); + + +/* get the description of an error number */ +const char *cdb_errmsg(int ecode); + + +/* set callback when error happened, 'cdb_deferrorcb' is optional, which shows the error to stderr */ +void cdb_seterrcb(CDB *db, CDB_ERRCALLBACK errcb, void *arg); + +/* a possible error callback */ +void cdb_deferrorcb(void *arg, int errno, const char *file, int line); + +#if defined(__cplusplus) +} +#endif + +#endif diff --git a/dap_chain_global_db.c b/dap_chain_global_db.c index 71b053b8efe5176800210862335b751b3b2565aa..3fa609bd65615e411a2204d39d44fc6b9b24e136 100755 --- a/dap_chain_global_db.c +++ b/dap_chain_global_db.c @@ -139,8 +139,8 @@ void dap_chain_global_db_objs_delete(dap_global_db_obj_t **objs) int dap_chain_global_db_init(dap_config_t * g_config) { const char *l_storage_path = dap_config_get_item_str(g_config, "resources", "dap_global_db_path"); - const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", - "sqlite"); + //const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", "sqlite"); + const char *l_driver_name = dap_config_get_item_str_default(g_config, "resources", "dap_global_db_driver", "cdb"); lock(); int res = dap_db_driver_init(l_driver_name, l_storage_path); //int res = dap_db_init(a_storage_path); diff --git a/dap_chain_global_db_driver.c b/dap_chain_global_db_driver.c index bb456a1f4595abc27f16b436d07fa2fb7d9fd605..612d04c02d6bd12517d0cdb3215012a5c269d4d4 100755 --- a/dap_chain_global_db_driver.c +++ b/dap_chain_global_db_driver.c @@ -34,6 +34,7 @@ #include "dap_hash.h" #include "dap_chain_global_db_driver_sqlite.h" +#include "dap_chain_global_db_driver_cdb.h" #include "dap_chain_global_db_driver.h" #define LOG_TAG "db_driver" @@ -79,6 +80,8 @@ int dap_db_driver_init(const char *a_driver_name, const char *a_filename_db) l_ret = -1; if(!dap_strcmp(s_used_driver, "sqlite")) l_ret = dap_db_driver_sqlite_init(a_filename_db, &s_drv_callback); + if(!dap_strcmp(s_used_driver, "cdb")) + l_ret = dap_db_driver_cdb_init(a_filename_db, &s_drv_callback); if(!l_ret) { pthread_condattr_t l_condattr; pthread_condattr_init(&l_condattr); diff --git a/dap_chain_global_db_driver_cdb.c b/dap_chain_global_db_driver_cdb.c new file mode 100644 index 0000000000000000000000000000000000000000..a16afebe43e4cb7bbed867ed44606848b107a6d8 --- /dev/null +++ b/dap_chain_global_db_driver_cdb.c @@ -0,0 +1,370 @@ +/* + * Authors: + * Konstantin Papizh <konstantin.papizh@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <stddef.h> +#include <string.h> +#include <dirent.h> +#include <sys/stat.h> +#include <uthash.h> +#include "dap_common.h" +#include "dap_hash.h" +#include "dap_strfuncs.h" +#include "dap_chain_global_db_driver_cdb.h" + +#define LOG_TAG "db_cdb" + +#define uint64_size sizeof(uint64_t) + +typedef struct _obj_arg { + pdap_store_obj_t o; + uint64_t q; + uint64_t n; +} obj_arg, *pobj_arg; + +typedef struct _cdb_instance { + CDB *cdb; + char *local_group; + UT_hash_handle hh; +} cdb_instance, *pcdb_instance; + +static char *s_cdb_path = NULL; +static pcdb_instance s_cdb = NULL; + +static inline void dap_cdb_uint_to_hex(char *arr, uint64_t val, short size) { + short i = 0; + for (i = 0; i < size; ++i) { + arr[i] = (char)(((uint64_t) val >> (8 * (size - 1 - i))) & 0xFFu); + } +} + +static inline uint64_t dap_cdb_hex_to_uint(char *arr, short size) { + uint64_t val = 0; + short i = 0; + for (i = 0; i < size; ++i){ + uint8_t byte = *arr++; + if (byte >= 'a' && byte <='f'){ + byte = byte - 'a' + 10; + } else if (byte >= 'A' && byte <='F') { + byte = byte - 'A' + 10; + } + val = (val << 8) | (byte & 0xFFu); + } + return val; +} + +static void cdb_serialize_val_to_dap_store_obj(pdap_store_obj_t a_obj, char *key, char *val) { + if (!key || !val) { + a_obj = NULL; + return; + } + int offset = 0; + a_obj->key = dap_strdup(key); + + unsigned char l_id[uint64_size] = {'\0'}; + memcpy(l_id, val, uint64_size); + a_obj->id = dap_cdb_hex_to_uint(l_id, uint64_size); + offset += uint64_size; + + unsigned char l_val_size[sizeof(unsigned long)] = {'\0'}; + memcpy(l_val_size, val + offset, sizeof(unsigned long)); + a_obj->value_len = dap_cdb_hex_to_uint(l_val_size, sizeof(unsigned long)); + offset += sizeof(unsigned long); + + a_obj->value = DAP_NEW_SIZE(uint8_t, a_obj->value_len); + memcpy(a_obj->value, val + offset, a_obj->value_len); + offset += a_obj->value_len; + + unsigned char l_rawtime[sizeof(time_t)] = {'\0'}; + memcpy(l_rawtime, val + offset, sizeof(time_t)); + a_obj->timestamp = dap_cdb_hex_to_uint(l_rawtime, sizeof(time_t)); +} + +pcdb_instance *dap_cdb_init_group(char *a_group, int a_flags) { + pcdb_instance l_cdb_i = DAP_NEW(cdb_instance); + l_cdb_i->local_group = dap_strdup(a_group); + l_cdb_i->cdb = cdb_new(); + char l_cdb_path[strlen(s_cdb_path) + strlen(a_group) + 2]; + memset(l_cdb_path, '\0', strlen(s_cdb_path) + strlen(a_group) + 2); + strcat(l_cdb_path, s_cdb_path); + strcat(l_cdb_path, "/"); + strcat(l_cdb_path, a_group); + cdb_options l_opts = { 1000000, 128, 1024 }; + if ((dap_db_driver_cdb_options(&l_opts, l_cdb_i->cdb) != CDB_SUCCESS) || + cdb_open(l_cdb_i->cdb, l_cdb_path, a_flags) < 0) + { + log_it(L_ERROR, "An error occured while opening CDB: \"%s\"", cdb_errmsg(cdb_errno(l_cdb_i->cdb))); + DAP_DELETE(l_cdb_i->local_group); + DAP_DELETE(l_cdb_i); + return NULL; + } + HASH_ADD_KEYPTR(hh, s_cdb, l_cdb_i->local_group, strlen(l_cdb_i->local_group), l_cdb_i); + return l_cdb_i; +} + +int dap_db_driver_cdb_init(const char *a_cdb_path, dap_db_driver_callbacks_t *a_drv_callback) { + s_cdb_path = dap_strdup(a_cdb_path); + if(s_cdb_path[strlen(s_cdb_path)] == '/') { + s_cdb_path[strlen(s_cdb_path)] = '\0'; + } + mkdir(s_cdb_path, 0755); + struct dirent *d; + DIR *dir = opendir(s_cdb_path); + if (!dir) { + log_it(L_ERROR, "Couldn't open db directory"); + return -1; + } + for (d = readdir(dir); d; d = readdir(dir)) { + if (!dap_strcmp(d->d_name, ".") || !dap_strcmp(d->d_name, "..")) { + continue; + } + pcdb_instance l_cdb_i = dap_cdb_init_group(d->d_name, CDB_CREAT | CDB_PAGEWARMUP); + if (!l_cdb_i) { + dap_db_driver_cdb_deinit(); + DAP_DELETE(s_cdb_path); + closedir(dir); + return -2; + } + CDBSTAT l_cdb_stat; + cdb_stat(l_cdb_i->cdb, &l_cdb_stat); + log_it(L_INFO, "Group \"%s\" found" , l_cdb_i->local_group); + log_it(L_INFO, "Records: %-24u" , l_cdb_stat.rnum); + log_it(L_INFO, "Average read latency: %-24u" , l_cdb_stat.rlatcy); + log_it(L_INFO, "Average write latency: %-24u" , l_cdb_stat.wlatcy); + } + a_drv_callback->read_last_store_obj = dap_db_driver_cdb_read_last_store_obj; + a_drv_callback->apply_store_obj = dap_db_driver_cdb_apply_store_obj; + a_drv_callback->read_store_obj = dap_db_driver_cdb_read_store_obj; + a_drv_callback->read_cond_store_obj = dap_db_driver_cdb_read_cond_store_obj; + a_drv_callback->deinit = dap_db_driver_cdb_deinit; + + closedir(dir); + return CDB_SUCCESS; +} + +CDB *dap_cdb_get_db_by_group(const char *a_group) { + pcdb_instance l_cdb_i = NULL; + HASH_FIND_STR(s_cdb, a_group, l_cdb_i); + if (!l_cdb_i) { + return NULL; + } + return l_cdb_i->cdb; +} + +int dap_cdb_add_group(const char *a_group) { + char l_cdb_path[strlen(s_cdb_path) + strlen(a_group) + 2]; + memset(l_cdb_path, '\0', strlen(s_cdb_path) + strlen(a_group) + 2); + strcat(l_cdb_path, s_cdb_path); + strcat(l_cdb_path, "/"); + strcat(l_cdb_path, a_group); + mkdir(l_cdb_path, 0755); + return 0; +} + +bool dap_cdb_get_last_obj_iter_callback(void *arg, const char *key, int ksize, const char *val, int vsize, uint32_t expire, uint64_t oid) { + /* this is wrong! TODO: instead of 'oid' must checkout real 'arg->id' */ + /*if (oid != ((pobj_arg)arg)->q) { + return true; + }*/ + cdb_serialize_val_to_dap_store_obj((pdap_store_obj_t)(((pobj_arg)arg)->o), key, val); + return false; +} + +bool dap_cdb_get_some_obj_iter_callback(void *arg, const char *key, int ksize, const char *val, int vsize, uint32_t expire, uint64_t oid) { + ((pobj_arg)arg)->q--; + uint64_t q = ((pobj_arg)arg)->q; + uint64_t n = ((pobj_arg)arg)->n; + pdap_store_obj_t l_obj = (pdap_store_obj_t)((pobj_arg)arg)->o; + cdb_serialize_val_to_dap_store_obj(&l_obj[n-q-1], key, val); + if (q == 0) { + return false; + } + return true; +} + +bool dap_cdb_get_cond_obj_iter_callback(void *arg, const char *key, int ksize, const char *val, int vsize, uint32_t expire, uint64_t oid) { + /* No need due to this implementation design */ +} + +int dap_db_driver_cdb_deinit() { + cdb_instance *cur_cdb, *tmp; + HASH_ITER(hh, s_cdb, cur_cdb, tmp) { + HASH_DEL(s_cdb, cur_cdb); + cdb_destroy(cur_cdb->cdb); + } + return CDB_SUCCESS; +} + +int dap_db_driver_cdb_options(pcdb_options l_opts, CDB* a_cdb) { + if (cdb_option(a_cdb, + l_opts->hsize, + l_opts->pcacheMB, + l_opts->rcacheMB) != CDB_SUCCESS) return -1; + return CDB_SUCCESS; +} + +dap_store_obj_t *dap_db_driver_cdb_read_last_store_obj(const char* a_group) { + if (!a_group) { + return NULL; + } + CDB *l_cdb = dap_cdb_get_db_by_group(a_group); + if (!l_cdb) { + return NULL; + } + CDBSTAT l_cdb_stat; + cdb_stat(l_cdb, &l_cdb_stat); + void *l_iter = cdb_iterate_new(l_cdb, l_cdb_stat.rnum); + obj_arg l_arg; + l_arg.o = DAP_NEW_Z(dap_store_obj_t); + l_arg.q = l_cdb_stat.rnum; + cdb_iterate(l_cdb, dap_cdb_get_last_obj_iter_callback, (void*)&l_arg, l_iter); + cdb_iterate_destroy(l_cdb, l_iter); + l_arg.o->group = dap_strdup(a_group); + return l_arg.o; +} + +dap_store_obj_t *dap_db_driver_cdb_read_store_obj(const char *a_group, const char *a_key, size_t *a_count_out) { + if (!a_group) { + return NULL; + } + //CDB_group l_group = dap_db_cdb_define_group(a_group); + CDB *l_cdb = dap_cdb_get_db_by_group(a_group); + if (!l_cdb) { + return NULL; + } + dap_store_obj_t *l_obj = NULL; + if (a_key) { + char *l_value; + int l_vsize; + cdb_get(l_cdb, a_key, strlen(a_key), (void**)&l_value, &l_vsize); + if (!l_value) { + return NULL; + } + l_obj = DAP_NEW_Z(dap_store_obj_t); + cdb_serialize_val_to_dap_store_obj(l_obj, a_key, l_value); + l_obj->group = dap_strdup(a_group); + cdb_free_val((void**)&l_value); + } else { + uint64_t l_count_out = 0; + if(a_count_out) { + l_count_out = *a_count_out; + } + CDBSTAT l_cdb_stat; + cdb_stat(l_cdb, &l_cdb_stat); + if ((l_count_out == 0) || (l_count_out > l_cdb_stat.rnum)) { + l_count_out = l_cdb_stat.rnum; + } + obj_arg l_arg; + l_arg.o = DAP_NEW_Z_SIZE(dap_store_obj_t, l_count_out * sizeof(dap_store_obj_t)); + l_arg.q = l_count_out; + l_arg.n = l_count_out; + void *l_iter = cdb_iterate_new(l_cdb, 0); + l_count_out = cdb_iterate(l_cdb, dap_cdb_get_some_obj_iter_callback, (void*)&l_arg, l_iter); + cdb_iterate_destroy(l_cdb, l_iter); + if(a_count_out) { + *a_count_out = l_count_out; + } + for (ulong i = 0; i < l_count_out; ++i) { + l_arg.o[i].group = dap_strdup(a_group); + } + l_obj = l_arg.o; + } + return l_obj; +} + +dap_store_obj_t* dap_db_driver_cdb_read_cond_store_obj(const char *a_group, uint64_t a_id, size_t *a_count_out) { + if (!a_group) { + return NULL; + } + CDB *l_cdb = dap_cdb_get_db_by_group(a_group); + if (!l_cdb) { + return NULL; + } + dap_store_obj_t *l_obj = NULL; + uint64_t l_count_out = 0; + if(a_count_out) { + l_count_out = *a_count_out; + } + CDBSTAT l_cdb_stat; + cdb_stat(l_cdb, &l_cdb_stat); + if ((l_count_out == 0) || (l_count_out > l_cdb_stat.rnum)) { + l_count_out = l_cdb_stat.rnum; + } + obj_arg l_arg; + l_arg.o = DAP_NEW_Z_SIZE(dap_store_obj_t, l_count_out * sizeof(dap_store_obj_t)); + l_arg.q = l_count_out; + l_arg.q = l_count_out; + void *l_iter = cdb_iterate_new(l_cdb, a_id + 1); // wrong! TODO: make use of obj->id + l_count_out = cdb_iterate(l_cdb, dap_cdb_get_some_obj_iter_callback, (void*)&l_arg, l_iter); + cdb_iterate_destroy(l_cdb, l_iter); + if(a_count_out) { + *a_count_out = l_count_out; + } + for (ulong i = 0; i < l_count_out; ++i) { + l_arg.o[i].group = dap_strdup(a_group); + } + l_obj = l_arg.o; +} + +int dap_db_driver_cdb_apply_store_obj(pdap_store_obj_t a_store_obj) { + if(!a_store_obj || !a_store_obj->group) { + return -1; + } + CDB *l_cdb = dap_cdb_get_db_by_group(a_store_obj->group); + if (!l_cdb) { + dap_cdb_add_group(a_store_obj->group); + pcdb_instance l_cdb_i = dap_cdb_init_group(a_store_obj->group, CDB_CREAT | CDB_PAGEWARMUP); + l_cdb = l_cdb_i->cdb; + } + if(a_store_obj->type == 'a') { + if(!a_store_obj->key || !a_store_obj->value || !a_store_obj->value_len) return -2; + cdb_record l_rec; + l_rec.key = dap_strdup(a_store_obj->key); + int offset = 0; + char *l_val = DAP_NEW_Z_SIZE(char, uint64_size + sizeof(unsigned long) + a_store_obj->value_len + sizeof(time_t)); + dap_cdb_uint_to_hex(l_val, a_store_obj->id, uint64_size); + offset += uint64_size; + + dap_cdb_uint_to_hex(l_val + offset, a_store_obj->value_len, sizeof(unsigned long)); + offset += sizeof(unsigned long); + + memcpy(l_val + offset, a_store_obj->value, a_store_obj->value_len); + offset += a_store_obj->value_len; + + unsigned long l_time = (unsigned long)a_store_obj->timestamp; + dap_cdb_uint_to_hex(l_val + offset, l_time, sizeof(time_t)); + offset += sizeof(time_t); + l_rec.val = l_val; + cdb_set(l_cdb, l_rec.key, strlen(l_rec.key), l_rec.val, offset); + DAP_DELETE(l_rec.key); + DAP_DELETE(l_rec.val); + } else if(a_store_obj->type == 'd') { + if(a_store_obj->key) { + cdb_del(l_cdb, a_store_obj->key, strlen(a_store_obj->key)); + } else { + cdb_destroy(l_cdb); + dap_cdb_init_group(a_store_obj->group, CDB_TRUNC | CDB_PAGEWARMUP); + } + } + return 0; +} diff --git a/dap_chain_global_db_driver_cdb.h b/dap_chain_global_db_driver_cdb.h new file mode 100644 index 0000000000000000000000000000000000000000..e24254f06d64ffd3e4b7d7589b05d3f691a97861 --- /dev/null +++ b/dap_chain_global_db_driver_cdb.h @@ -0,0 +1,46 @@ +/* + * Authors: + * Konstantin Papizh <konstantin.papizh@demlabs.net> + * DeM Labs Inc. https://demlabs.net + * Kelvin Project https://github.com/kelvinblockchain + * Copyright (c) 2019 + * All rights reserved. + + This file is part of DAP (Deus Applications Prototypes) the open source project + + DAP (Deus Applicaions Prototypes) 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. + + DAP 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 any DAP based project. If not, see <http://www.gnu.org/licenses/>. + */ + +#include "cuttdb.h" +#include "dap_chain_global_db_driver.h" + +typedef struct _cdb_options { + int hsize; // Main hash table size, 1%-10% of total records, immutable + int rcacheMB; // Record cache in MBytes + int pcacheMB; // Index page cache in MBytes +} cdb_options, *pcdb_options; + +typedef struct _cdb_record { + char *key; + char *val; +} cdb_record, *pcdb_record; + +int dap_db_driver_cdb_init(const char*, dap_db_driver_callbacks_t*); +int dap_db_driver_cdb_deinit(); + +int dap_db_driver_cdb_apply_store_obj(pdap_store_obj_t); + +dap_store_obj_t *dap_db_driver_cdb_read_last_store_obj(const char*); +dap_store_obj_t *dap_db_driver_cdb_read_store_obj(const char*, const char*, size_t*); +dap_store_obj_t* dap_db_driver_cdb_read_cond_store_obj(const char*, uint64_t, size_t*); diff --git a/libcuttdb.a b/libcuttdb.a new file mode 100644 index 0000000000000000000000000000000000000000..eb6157bf89f6a454980eafe4af9f5a92651f63fe Binary files /dev/null and b/libcuttdb.a differ