1 Star 0 Fork 1

ZHANGJUN1990/ekvdb

forked from arrco/ekvdb 
加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
ekvdb.c 39.98 KB
一键复制 编辑 原始数据 按行查看 历史
arrco 提交于 2022-09-12 17:05 . 首版本V1.0.0
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230
/*
* Copyright (c) 2022 Hong Jiahua
* https://gitee.com/arrco/ekvdb
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include "ekvdb.h"
/************************************************************************/
/* */
/************************************************************************/
#define EKVDB_HEADER_MAGIC 0x6EADEAEA
#define EKVDB_EXIST_MAGIC 0xE35157EA
#define EKVDB_COUNT_MAGIC 0xFFFFFFFF
#define EKVDB_INDEX "/ekvdb.index"
#define EKVDB_DATA "/ekvdb.data"
#define EKVDB_MAX_NAME EKVDB_INDEX
#define EKVDB_INDEX_BACK "/ekvdb.index.back"
#define EKVDB_DATA_BACK "/ekvdb.data.back"
#define EKVDB_MAX_NAME_BACK EKVDB_INDEX_BACK
#define EKVDB_LIST_NULL 0
#define EKVDB_TEMPBUF_LEN 512
#define EKVDB_HASH hash
typedef struct
{
uint32_t header;
uint32_t len;
uint32_t revlen;
uint32_t listlen;
uint32_t revlistlen;
uint32_t threshold;
uint32_t count;
uint32_t redundant;
} ekvdb_index_header_t;
typedef struct
{
uint32_t hash_a;
uint32_t hash_b;
uint32_t exist;
uint32_t keyoffset;
uint32_t dataoffset;
uint32_t datalen;
uint32_t next;
} ekvdb_index_t;
static uint32_t ekvdb_FNV_hash(char *str)
{
uint32_t hash = 2166136261;
while (*str) {
hash *= 16777619;
hash ^= (*str++);
}
return hash;
}
static uint32_t ekvdb_FNV_hash_next(uint32_t hash, char *str)
{
while (*str) {
hash *= 16777619;
hash ^= (*str++);
}
return hash;
}
static uint32_t ekvdb_RS_hash(char *str)
{
uint32_t hash = 0;
uint32_t magic = 63689;
while (*str) {
hash = hash * magic + (*str++);
magic *= 378551;
}
return hash;
}
static uint32_t ekvdb_DJB2_hash(char *str)
{
uint32_t hash = 5381;
while (*str) {
hash = hash * 33 ^ (*str++);
}
return hash;
}
/************************************************************************/
/* */
/************************************************************************/
static inline uint32_t ekvdb_hash_table(char *str)
{
return ekvdb_FNV_hash(str);
}
static inline uint32_t ekvdb_hash_table_next(uint32_t hash, char *str)
{
return ekvdb_FNV_hash_next(hash, str);
}
static inline uint32_t ekvdb_hash_a(char *str)
{
return ekvdb_RS_hash(str);
}
static inline uint32_t ekvdb_hash_b(char *str)
{
return ekvdb_DJB2_hash(str);
}
/**
* @brief 创建数据库
* @param[in] path 路径
* @param[in] len 数据库存储键值对的数量
* @param[in] threshold 自动清除冗余数据的阈值
*
* @return errcode
* @retval 0 成功
* @retval -1 失败
*/
int ekvdb_create(char* path, uint32_t len, uint32_t threshold)
{
FILE * dbindex_fp;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "w+");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
ekvdb_index_t filldata = {0};
fseek(dbindex_fp, 0, SEEK_SET);
header.header = EKVDB_HEADER_MAGIC;
header.len = len * 4 / 3; //load factor (0.75)
header.revlen = ~header.len;
header.listlen = len;
header.revlistlen = ~header.listlen;
header.threshold = threshold;
#if EKVDB_AUTO_COUNT_SUPPORT
header.count = 0;
#else
header.count = EKVDB_COUNT_MAGIC;
#endif
header.redundant = 0;
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
for(int i = 0; i < (header.len + header.listlen); i++)
fwrite(&filldata, 1, sizeof(ekvdb_index_t), dbindex_fp);
fclose(dbindex_fp);
return 0;
}
/**
* @brief 读数据库记录
* @param[in] path 路径
* @param[in] key 键
* @param[in] val 值
* @param[in] vallen 值的长度
*
* @return errcode
* @retval >=0 成功读取的值长度
* @retval -1 失败
*/
int ekvdb_read(char* path, char* key, void* val, uint32_t vallen)
{
FILE * dbindex_fp;
FILE * dbdata_fp;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "r");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
sprintf(filename, "%s%s", path, EKVDB_DATA);
dbdata_fp = fopen(filename, "r");
if(dbdata_fp == NULL) {
fclose(dbindex_fp);
return -1;
}
uint32_t hash = ekvdb_hash_table(key);
uint32_t hash_a = ekvdb_hash_a(key);
uint32_t hash_b = ekvdb_hash_b(key);
uint32_t hash_pos = EKVDB_HASH % header.len;
ekvdb_index_t readdata;
do {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_t)) {
goto error_end;
}
if(readdata.exist == EKVDB_EXIST_MAGIC) {
if (readdata.hash_a == hash_a && readdata.hash_b == hash_b) {
break;
} else {
hash_pos = readdata.next;
if (hash_pos == EKVDB_LIST_NULL) {
goto error_end;
}
}
} else {
goto error_end;
}
} while(1);
fseek(dbdata_fp, readdata.dataoffset, SEEK_SET);
ret = fread(val, 1, readdata.datalen <= vallen ? readdata.datalen : vallen, dbdata_fp);
fclose(dbindex_fp);
fclose(dbdata_fp);
return ret;
error_end:
fclose(dbindex_fp);
fclose(dbdata_fp);
return -1;
}
/**
* @brief 写数据库数据
* @param[in] path 路径
* @param[in] key 键
* @param[in] val 值
* @param[in] vallen 值的长度
*
* @return errcode
* @retval >=0 成功写入的值长度
* @retval -1 失败
*/
int ekvdb_write(char* path, char* key, void* val, uint32_t vallen)
{
FILE * dbindex_fp;
FILE * dbdata_fp;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "rw+");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
sprintf(filename, "%s%s", path, EKVDB_DATA);
dbdata_fp = fopen(filename, "rw+");
if(dbdata_fp == NULL) {
dbdata_fp = fopen(filename, "w+");
if(dbdata_fp == NULL) {
fclose(dbindex_fp);
return -1;
}
}
fseek(dbdata_fp, 0, SEEK_END);
uint32_t offset = ftell(dbdata_fp);
uint32_t hash = ekvdb_hash_table(key);
uint32_t hash_a = ekvdb_hash_a(key);
uint32_t hash_b = ekvdb_hash_b(key);
uint32_t hash_pos = EKVDB_HASH % header.len;
ekvdb_index_t data = {0};
do {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&data, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_t)) {
memset(&data, 0, sizeof(ekvdb_index_t));
break;
}
if(data.exist == EKVDB_EXIST_MAGIC) {
if (data.hash_a == hash_a && data.hash_b == hash_b) {
break;
} else {
if (data.next == EKVDB_LIST_NULL) {
uint32_t hash_list_start = hash % header.listlen;
uint32_t hash_list_pos = hash_list_start;
ekvdb_index_t newdata;
#if EKVDB_AUTO_EXTEND_SUPPORT
int extend_flag = 0;
#endif
do {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + (hash_list_pos + header.len) * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&newdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_t)) {
memset(&newdata, 0, sizeof(ekvdb_index_t));
break;
}
if(newdata.exist == EKVDB_EXIST_MAGIC) {
hash_list_pos = (hash_list_pos + 1) % header.listlen;
if (hash_list_pos == hash_list_start) {
fclose(dbindex_fp);
#if EKVDB_AUTO_EXTEND_SUPPORT
if(extend_flag) {
fclose(dbdata_fp);
return -1;
}
extend_flag = 1;
ret = ekvdb_quick_extend(path);
if(ret < 0) {
fclose(dbdata_fp);
return -1;
}
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "rw+");
if(dbindex_fp == NULL) {
fclose(dbdata_fp);
return -1;
}
memset(&header, 0, sizeof(ekvdb_index_header_t));
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
fclose(dbdata_fp);
return -1;
}
hash_list_start = hash % header.listlen;
hash_list_pos = hash_list_start;
#else
fclose(dbdata_fp);
return -1;
#endif
}
} else {
break;
}
} while(1);
data.next = hash_list_pos + header.len;
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
fwrite(&data, 1, sizeof(ekvdb_index_t), dbindex_fp);
hash_pos = data.next;
break;
} else {
hash_pos = data.next;
continue;
}
}
} else {
break;
}
} while(1);
if(data.exist != EKVDB_EXIST_MAGIC || data.hash_a != hash_a || data.hash_b != hash_b) {
data.exist = EKVDB_EXIST_MAGIC;
data.hash_a = hash_a;
data.hash_b = hash_b;
data.next = EKVDB_LIST_NULL;
}
#if EKVDB_REDUNDANT_SUPPORT
else {
header.redundant += (data.datalen + (data.dataoffset - data.keyoffset));
}
#endif
uint32_t writelen;
data.keyoffset = offset;
writelen = fwrite(key, 1, strlen(key) + 1, dbdata_fp);
data.dataoffset = offset + writelen;
data.datalen = fwrite(val, 1, vallen, dbdata_fp);
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
ret = fwrite(&data, 1, sizeof(ekvdb_index_t), dbindex_fp);
#if EKVDB_AUTO_COUNT_SUPPORT
if(header.count != EKVDB_COUNT_MAGIC) {
header.count++;
fseek(dbindex_fp, 0, SEEK_SET);
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
}
#else
if(header.count != EKVDB_COUNT_MAGIC) {
header.count = EKVDB_COUNT_MAGIC;
fseek(dbindex_fp, 0, SEEK_SET);
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
}
#endif
fclose(dbindex_fp);
fclose(dbdata_fp);
#if EKVDB_AUTO_COMPACT_SUPPORT
if(header.threshold && (header.redundant > header.threshold)) {
ekvdb_compact(path);
}
#endif
return data.datalen;
}
/**
* @brief 删除数据库记录
* @param[in] path 路径
* @param[in] key 键
*
* @return errcode
* @retval 0 成功
* @retval -1 失败
*/
int ekvdb_delete(char* path, char* key)
{
FILE * dbindex_fp;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "rw+");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
uint32_t hash = ekvdb_hash_table(key);
uint32_t hash_a = ekvdb_hash_a(key);
uint32_t hash_b = ekvdb_hash_b(key);
uint32_t hash_pos = EKVDB_HASH % header.len;
uint32_t hash_last_pos = hash_pos;
ekvdb_index_t readdata;
do {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_t)) {
fclose(dbindex_fp);
return -1;
}
if(readdata.exist == EKVDB_EXIST_MAGIC) {
if (readdata.hash_a == hash_a && readdata.hash_b == hash_b) {
uint32_t tmpnext = readdata.next;
#if EKVDB_REDUNDANT_SUPPORT
header.redundant += (readdata.datalen + (readdata.dataoffset - readdata.keyoffset));
#endif
readdata.exist = 0;
readdata.hash_a = 0;
readdata.hash_b = 0;
readdata.keyoffset = 0;
readdata.dataoffset = 0;
readdata.datalen = 0;
readdata.next = EKVDB_LIST_NULL;
if(hash_last_pos == hash_pos && tmpnext != EKVDB_LIST_NULL) {
ekvdb_index_t lastdata;
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + tmpnext * sizeof(ekvdb_index_t), SEEK_SET);
fread(&lastdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + tmpnext * sizeof(ekvdb_index_t), SEEK_SET);
fwrite(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
readdata = lastdata;
}
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
fwrite(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(hash_last_pos != hash_pos) {
ekvdb_index_t lastdata;
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + hash_last_pos * sizeof(ekvdb_index_t), SEEK_SET);
fread(&lastdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
lastdata.next = tmpnext;
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + hash_last_pos * sizeof(ekvdb_index_t), SEEK_SET);
fwrite(&lastdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
}
break;
} else {
hash_last_pos = hash_pos;
hash_pos = readdata.next;
if (hash_pos == EKVDB_LIST_NULL) {
fclose(dbindex_fp);
return -1;
}
}
} else {
fclose(dbindex_fp);
return -1;
}
} while(1);
#if EKVDB_AUTO_COUNT_SUPPORT
if(header.count > 0 && header.count != EKVDB_COUNT_MAGIC) {
header.count--;
fseek(dbindex_fp, 0, SEEK_SET);
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
}
#else
if(header.count != EKVDB_COUNT_MAGIC) {
header.count = EKVDB_COUNT_MAGIC;
fseek(dbindex_fp, 0, SEEK_SET);
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
}
#endif
fclose(dbindex_fp);
return 0;
}
/**
* @brief 清除数据库的冗余记录
* @param[in] path 路径
*
* @return errcode
* @retval 0 成功
* @retval -1 失败
*/
int ekvdb_compact(char* path)
{
FILE * dbindex_fp;
FILE * dbdata_fp;
FILE * dbindex_back_fp;
FILE * dbdata_back_fp;
char tempbuf[EKVDB_TEMPBUF_LEN];
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "r");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
sprintf(filename, "%s%s", path, EKVDB_DATA);
dbdata_fp = fopen(filename, "r");
if(dbdata_fp == NULL) {
fclose(dbindex_fp);
return -1;
}
char backfilename[strlen(path) + strlen(EKVDB_MAX_NAME_BACK) + 1];
sprintf(backfilename, "%s%s", path, EKVDB_INDEX_BACK);
dbindex_back_fp = fopen(backfilename, "w+");
if(dbindex_back_fp == NULL) {
fclose(dbindex_fp);
fclose(dbdata_fp);
return -1;
}
ekvdb_index_t filldata = {0};
#if EKVDB_REDUNDANT_SUPPORT
header.redundant = 0;
#endif
fseek(dbindex_back_fp, 0, SEEK_SET);
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_back_fp);
for(int i = 0; i < (header.len + header.listlen); i++)
fwrite(&filldata, 1, sizeof(ekvdb_index_t), dbindex_back_fp);
sprintf(backfilename, "%s%s", path, EKVDB_DATA_BACK);
dbdata_back_fp = fopen(backfilename, "w+");
if(dbdata_back_fp == NULL) {
fclose(dbindex_fp);
fclose(dbdata_fp);
fclose(dbindex_back_fp);
sprintf(backfilename, "%s%s", path, EKVDB_INDEX_BACK);
remove(backfilename);
return -1;
}
ekvdb_index_t readdata;
uint32_t write_pos = 0;
for(int i = 0; i < header.len; i++) {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + i * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_t)) {
fclose(dbindex_fp);
fclose(dbdata_fp);
fclose(dbdata_back_fp);
remove(backfilename);
fclose(dbindex_back_fp);
sprintf(backfilename, "%s%s", path, EKVDB_INDEX_BACK);
remove(backfilename);
return -1;
}
if(readdata.exist == EKVDB_EXIST_MAGIC) {
uint32_t readlen = readdata.datalen + (readdata.dataoffset - readdata.keyoffset);
uint32_t writelen;
fseek(dbdata_fp, readdata.keyoffset, SEEK_SET);
fseek(dbindex_back_fp, sizeof(ekvdb_index_header_t) + i * sizeof(ekvdb_index_t), SEEK_SET);
readdata.dataoffset = write_pos + (readdata.dataoffset - readdata.keyoffset);
readdata.keyoffset = write_pos;
fwrite(&readdata, 1, sizeof(ekvdb_index_t), dbindex_back_fp);
while(readlen) {
writelen = readlen > EKVDB_TEMPBUF_LEN ? EKVDB_TEMPBUF_LEN : readlen;
fread(tempbuf, 1, writelen, dbdata_fp);
fwrite(tempbuf, 1, writelen, dbdata_back_fp);
readlen -= writelen;
write_pos += writelen;
}
while(readdata.next != EKVDB_LIST_NULL) {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + readdata.next * sizeof(ekvdb_index_t), SEEK_SET);
fseek(dbindex_back_fp, sizeof(ekvdb_index_header_t) + readdata.next * sizeof(ekvdb_index_t), SEEK_SET);
fread(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
fseek(dbdata_fp, readdata.keyoffset, SEEK_SET);
readlen = readdata.datalen + readdata.dataoffset - readdata.keyoffset;
readdata.dataoffset = write_pos + (readdata.dataoffset - readdata.keyoffset);
readdata.keyoffset = write_pos;
fwrite(&readdata, 1, sizeof(ekvdb_index_t), dbindex_back_fp);
while(readlen) {
writelen = readlen > EKVDB_TEMPBUF_LEN ? EKVDB_TEMPBUF_LEN : readlen;
fread(tempbuf, 1, writelen, dbdata_fp);
fwrite(tempbuf, 1, writelen, dbdata_back_fp);
readlen -= writelen;
write_pos += writelen;
}
}
}
}
fclose(dbindex_fp);
fclose(dbdata_fp);
fclose(dbindex_back_fp);
sprintf(filename, "%s%s", path, EKVDB_INDEX);
remove(filename);
sprintf(backfilename, "%s%s", path, EKVDB_INDEX_BACK);
rename(backfilename, filename);
fclose(dbdata_back_fp);
sprintf(filename, "%s%s", path, EKVDB_DATA);
remove(filename);
sprintf(backfilename, "%s%s", path, EKVDB_DATA_BACK);
rename(backfilename, filename);
return 0;
}
/**
* @brief 快速扩展数据库容量
* @param[in] path 路径
*
* @return errcode
* @retval 0 成功
* @retval -1 失败
*/
int ekvdb_quick_extend(char* path)
{
FILE * dbindex_fp;
uint32_t oldlistlen;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "rw+");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
ekvdb_index_t filldata = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
oldlistlen = header.listlen;
header.listlen <<= 1;
header.revlistlen = ~header.listlen;
fseek(dbindex_fp, 0, SEEK_SET);
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + (header.len + oldlistlen) * sizeof(ekvdb_index_t), SEEK_SET);
for(int i = 0; i < (header.listlen - oldlistlen); i++)
fwrite(&filldata, 1, sizeof(ekvdb_index_t), dbindex_fp);
fclose(dbindex_fp);
return 0;
}
/**
* @brief 扩展数据库容量
* @param[in] path 路径
*
* @return errcode
* @retval 0 成功
* @retval -1 失败
*/
int ekvdb_extend(char* path)
{
FILE * dbindex_fp;
FILE * dbdata_fp;
FILE * dbindex_back_fp;
char tempbuf[EKVDB_TEMPBUF_LEN];
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "r");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
sprintf(filename, "%s%s", path, EKVDB_DATA);
dbdata_fp = fopen(filename, "r");
if(dbdata_fp == NULL) {
fclose(dbindex_fp);
return -1;
}
char backfilename[strlen(path) + strlen(EKVDB_MAX_NAME_BACK) + 1];
sprintf(backfilename, "%s%s", path, EKVDB_INDEX_BACK);
dbindex_back_fp = fopen(backfilename, "w+");
if(dbindex_back_fp == NULL) {
fclose(dbindex_fp);
fclose(dbdata_fp);
return -1;
}
ekvdb_index_t filldata = {0};
uint32_t headerlen = header.len;
if(header.listlen > header.len) {
header.len = header.listlen * 4 / 3;
header.revlen = ~header.len;
} else {
header.len <<= 1;
header.revlen = ~header.len;
header.listlen <<= 1;
header.revlistlen = ~header.listlen;
}
fseek(dbindex_back_fp, 0, SEEK_SET);
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_back_fp);
for(int i = 0; i < (header.len + header.listlen); i++)
fwrite(&filldata, 1, sizeof(ekvdb_index_t), dbindex_back_fp);
uint32_t table_pos, hash_pos;
ekvdb_index_t readdata = {0};
ekvdb_index_t checkdata = {0};
uint32_t key_len = 0;
uint32_t hash;
uint32_t read_next;
for(int i = 0; i < headerlen; i++) {
table_pos = i;
do {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + table_pos * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_t)) {
goto error_end;
}
if(readdata.exist == EKVDB_EXIST_MAGIC) {
key_len = readdata.dataoffset - readdata.keyoffset;
fseek(dbdata_fp, readdata.keyoffset, SEEK_SET);
if(EKVDB_TEMPBUF_LEN >= key_len) {
fread(tempbuf, 1, key_len, dbdata_fp);
hash = ekvdb_hash_table(tempbuf);
} else {
uint32_t read_pos = key_len, read_len = 0;
read_len = EKVDB_TEMPBUF_LEN - 1;
read_pos -= read_len;
fread(tempbuf, 1, read_len, dbdata_fp);
tempbuf[EKVDB_TEMPBUF_LEN - 1] = 0;
hash = ekvdb_hash_table(tempbuf);
do {
read_len = read_pos > EKVDB_TEMPBUF_LEN - 1 ? EKVDB_TEMPBUF_LEN - 1 : read_pos;
read_pos -= read_len;
fread(tempbuf, 1, read_len, dbdata_fp);
tempbuf[EKVDB_TEMPBUF_LEN - 1] = 0;
hash = ekvdb_hash_table_next(hash, tempbuf);
} while(read_pos);
}
hash_pos = hash % header.len;
do {
fseek(dbindex_back_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&checkdata, 1, sizeof(ekvdb_index_t), dbindex_back_fp);
if(ret != sizeof(ekvdb_index_t)) {
goto error_end;
}
if(checkdata.exist == EKVDB_EXIST_MAGIC) {
if (checkdata.next == EKVDB_LIST_NULL) {
uint32_t hash_list_start = hash % header.listlen;
uint32_t hash_list_pos = hash_list_start;
ekvdb_index_t newdata;
do {
fseek(dbindex_back_fp, sizeof(ekvdb_index_header_t) + (hash_list_pos + header.len) * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&newdata, 1, sizeof(ekvdb_index_t), dbindex_back_fp);
if(ret != sizeof(ekvdb_index_t)) {
goto error_end;
}
if(newdata.exist == EKVDB_EXIST_MAGIC) {
hash_list_pos = (hash_list_pos + 1) % header.listlen;
if (hash_list_pos == hash_list_start) {
goto error_end;
}
} else {
break;
}
} while(1);
checkdata.next = hash_list_pos + header.len;
fseek(dbindex_back_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
fwrite(&checkdata, 1, sizeof(ekvdb_index_t), dbindex_back_fp);
hash_pos = checkdata.next;
break;
} else {
hash_pos = checkdata.next;
}
} else {
break;
}
} while(1);
read_next = readdata.next;
readdata.next = EKVDB_LIST_NULL;
fseek(dbindex_back_fp, sizeof(ekvdb_index_header_t) + hash_pos * sizeof(ekvdb_index_t), SEEK_SET);
fwrite(&readdata, 1, sizeof(ekvdb_index_t), dbindex_back_fp);
if(read_next != EKVDB_LIST_NULL) {
table_pos = read_next;
} else {
break;
}
} else {
break;
}
} while(1);
}
fclose(dbindex_fp);
fclose(dbdata_fp);
fclose(dbindex_back_fp);
sprintf(filename, "%s%s", path, EKVDB_INDEX);
remove(filename);
sprintf(backfilename, "%s%s", path, EKVDB_INDEX_BACK);
rename(backfilename, filename);
return 0;
error_end:
fclose(dbindex_fp);
fclose(dbdata_fp);
fclose(dbindex_back_fp);
sprintf(backfilename, "%s%s", path, EKVDB_INDEX_BACK);
remove(backfilename);
return -1;
}
/**
* @brief 重置数据库
* @param[in] path 路径
*
* @return errcode
* @retval 0 成功
* @retval -1 失败
*/
int ekvdb_reset(char* path)
{
FILE * dbindex_fp;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "rw+");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
ekvdb_index_t filldata = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
fclose(dbindex_fp);
dbindex_fp = fopen(filename, "w+");
if(dbindex_fp == NULL) {
return -1;
}
#if EKVDB_AUTO_COUNT_SUPPORT
header.count = 0;
#else
header.count = EKVDB_COUNT_MAGIC;
#endif
fseek(dbindex_fp, 0, SEEK_SET);
fwrite(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
for(int i = 0; i < (header.len + header.listlen); i++)
fwrite(&filldata, 1, sizeof(ekvdb_index_t), dbindex_fp);
fclose(dbindex_fp);
sprintf(filename, "%s%s", path, EKVDB_DATA);
remove(filename);
return 0;
}
/**
* @brief 清空数据库
* @param[in] path 路径
*
* @return 无
*/
void ekvdb_clear(char* path)
{
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
remove(filename);
sprintf(filename, "%s%s", path, EKVDB_DATA);
remove(filename);
}
/**
* @brief 数据库计数
* @param[in] path 路径
*
* @return errcode
* @retval >=0 数据库内存储的数据数量
* @retval -1 失败
*/
int ekvdb_count(char* path)
{
#if EKVDB_AUTO_COUNT_SUPPORT
FILE * dbindex_fp;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "r");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
fclose(dbindex_fp);
if(header.count != EKVDB_COUNT_MAGIC)
return header.count;
else
return -1;
#else
return -1;
#endif
}
/**
* @brief 数据库容量
* @param[in] path 路径
*
* @return errcode
* @retval >=0 数据库的容量
* @retval -1 失败
*/
int ekvdb_capacity(char* path)
{
FILE * dbindex_fp;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "r");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
fclose(dbindex_fp);
return header.listlen;
}
/**
* @brief 数据库数据冗余
* @param[in] path 路径
*
* @return errcode
* @retval >=0 数据库的冗余
* @retval -1 失败
*/
int ekvdb_redundant(char* path)
{
#if EKVDB_REDUNDANT_SUPPORT
FILE * dbindex_fp;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "r");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
fclose(dbindex_fp);
return header.redundant;
#else
return -1;
#endif
}
/**
* @brief 数据库迭代初始化
* @param[in] path 路径
* @param[in] iterator 迭代器
*
* @return errcode
* @retval 0 成功
* @retval -1 失败
*/
int ekvdb_iterator_init(char* path, ekvdb_iterator_t* iterator)
{
if(iterator == NULL)
return -1;
iterator->pos = 0;
iterator->next = EKVDB_LIST_NULL;
return 0;
}
/**
* @brief 数据库迭代获取下一个键值对
* @param[in] path 路径
* @param[in] iterator 迭代器
* @param[in] key 键的缓冲区,用于存放下一个键,如果键的缓冲区大小无法存放下键,则最后一个Byte的值不为0
* @param[in] keymaxlen 键的缓冲区大小
* @param[in] val 值的缓冲区,用于存放下一个值
* @param[in] valmaxlen 值的缓冲区大小
*
* @return errcode
* @retval >=0 获取值的大小
* @retval -1 失败
*/
int ekvdb_iterator_next(char* path, ekvdb_iterator_t* iterator, char* key, uint32_t keymaxlen, void* val, uint32_t valmaxlen)
{
FILE * dbindex_fp;
int ret;
char filename[strlen(path) + strlen(EKVDB_MAX_NAME) + 1];
sprintf(filename, "%s%s", path, EKVDB_INDEX);
dbindex_fp = fopen(filename, "r");
if(dbindex_fp == NULL) {
return -1;
}
ekvdb_index_header_t header = {0};
fseek(dbindex_fp, 0, SEEK_SET);
ret = fread(&header, 1, sizeof(ekvdb_index_header_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_header_t) || header.header != EKVDB_HEADER_MAGIC || header.revlen != (~header.len) || header.revlistlen != (~header.listlen)) {
fclose(dbindex_fp);
return -1;
}
if(iterator->pos >= header.len) {
fclose(dbindex_fp);
return -1;
}
ekvdb_index_t readdata = {0};
if(iterator->next != EKVDB_LIST_NULL) {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + iterator->next * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_t)) {
fclose(dbindex_fp);
return -1;
}
iterator->next = readdata.next;
} else {
for(; iterator->pos < header.len; iterator->pos++) {
fseek(dbindex_fp, sizeof(ekvdb_index_header_t) + iterator->pos * sizeof(ekvdb_index_t), SEEK_SET);
ret = fread(&readdata, 1, sizeof(ekvdb_index_t), dbindex_fp);
if(ret != sizeof(ekvdb_index_t)) {
fclose(dbindex_fp);
return -1;
}
if(readdata.exist == EKVDB_EXIST_MAGIC) {
iterator->next = readdata.next;
break;
}
}
}
if(iterator->next == EKVDB_LIST_NULL) {
iterator->pos++;
}
if(readdata.exist == EKVDB_EXIST_MAGIC) {
FILE * dbdata_fp;
sprintf(filename, "%s%s", path, EKVDB_DATA);
dbdata_fp = fopen(filename, "r");
if(dbdata_fp == NULL) {
fclose(dbindex_fp);
return -1;
}
uint32_t keylen = readdata.dataoffset - readdata.keyoffset;
key[keymaxlen - 1] = 0;
fseek(dbdata_fp, readdata.keyoffset, SEEK_SET);
ret = fread(key, 1, keylen <= keymaxlen ? keylen : keymaxlen, dbdata_fp);
fseek(dbdata_fp, readdata.dataoffset, SEEK_SET);
ret = fread(val, 1, readdata.datalen <= valmaxlen ? readdata.datalen : valmaxlen, dbdata_fp);
fclose(dbdata_fp);
} else {
ret = -1;
}
fclose(dbindex_fp);
return ret;
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C
1
https://gitee.com/zhangjun1990/ekvdb.git
git@gitee.com:zhangjun1990/ekvdb.git
zhangjun1990
ekvdb
ekvdb
master

搜索帮助