一个leveldb C api的包装

在 C 中调用 leveldb,把繁琐隐藏起来。提供了一个高级的 API 接口。如下的调用方式:

  

LeveldbCtx dbctx;
if (LeveldbCtxCreate("C:\\Temp\\test.cachedb", LDBCTX_ACCESSMODE_ALL, LDBCTX_OFLAG_CREATE_IF_MISSING, -1, 0, 0, 0, 0, 0, &dbctx) != LDBCTX_SUCCESS) {
exit(EXIT_FAILURE);
} ...
LeveldbCtxDestroy(dbctx, 0);

 

/***********************************************************************
* Copyright (c) 2008-2080 350137278@
*
* ALL RIGHTS RESERVED.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**********************************************************************/
/**
* @filename leveldb_c_wrapper.h
* A wrapper for leveldb for C for both Windows and Linux.
*
* C bindings for leveldb may be useful as a stable ABI that can be
* used by programs that keep leveldb in a shared library, or for JNI.
*
* A leveldb database may only be opened by one process at a time.
*
* The leveldb implementation acquires a lock from the operating system
* to prevent misuse.
*
* Within a single process, the same leveldb::DB object may be safely shared
* by multiple concurrent threads.I.e., different threads may write into or
* fetch iterators or call Get on the same database without any external
* synchronization(the leveldb implementation will automatically do the
* required synchronization).
*
* However other objects(like Iterator and WriteBatch) may require external
* synchronization.
*
* If two threads share such an object, they must protect access to it using
* their own locking protocol.
*
* More details are available in the public header files. See also:
* https:///google/leveldb/blob/master/doc/#concurrency
*
* @author Liang Zhang <350137278@>
* @version 0.0.1
* @create 2019-11-14
* @update 2019-10-14
*/
#ifndef LEVELDB_C_WRAPPER_H_INCLUDED
#define LEVELDB_C_WRAPPER_H_INCLUDED

#if defined(__cplusplus)
extern "C"
{
#endif

#include <leveldb/c.h>

#include "thread_swlock.h"


/**
* refer:

#define LDBCTX_DBPATH_MAX 1024
#define LDBCTX_ERRMSG_LEN 255


/**
* default size in bytes for leveldb cache is 256 MB
*/
#define LDBCTX_CACHE_CAPACITY_DEFAULT 268435456

/**
* access mode
*/
#define LDBCTX_ACCESSMODE_READ 1
#define LDBCTX_ACCESSMODE_WRITE 2
#define LDBCTX_ACCESSMODE_ALL (LDBCTX_ACCESSMODE_READ | LDBCTX_ACCESSMODE_WRITE)


/**
* leveldb options flags
*/
#define LDBCTX_OFLAG_CREATE_IF_MISSING 1
#define LDBCTX_OFLAG_ERROR_IF_EXISTS 2
#define LDBCTX_OFLAG_PARANOID_CHECKS 4
#define LDBCTX_OFLAG_SNAPPY_COMPRESSION 8

#define LDBCTX_OFLAG_READ_VERIFY_CHECKSUMS 16
#define LDBCTX_OFLAG_READ_FILL_CACHE 32

/**
* By default, each write to leveldb is asynchronous. Asynchronous writes
* are often more than a thousand times as fast as synchronous writes.
*/
#define LDBCTX_OFLAG_WRITE_SYNC 64


#define LDBCTX_OFLAG_CHECK(optionsflags, opflag) ((unsigned char)((optionsflags) & (opflag) ? 1 : 0))


/**
* api result
*/
typedef int LDBCTX_RESULT;

#define LDBCTX_SUCCESS 0
#define LDBCTX_ERROR (-1)

#define LDBCTX_ERROR_LEVELDB (-2)
#define LDBCTX_ERROR_INVARG (-3)
#define LDBCTX_ERROR_NOMEM (-4)

typedef struct _LeveldbContext_t * LeveldbCtx;


typedef struct _LeveldbContext_t
{
ThreadLock_t ldbLock;

char * err;
leveldb_env_t * env;
leveldb_t * ldb;

leveldb_options_t * options;
leveldb_readoptions_t * readoptions;
leveldb_writeoptions_t * writeoptions;

size_t cachecapacity;
leveldb_cache_t * cache;

char errmsg[LDBCTX_ERRMSG_LEN + 1];

int pathlen;
char dbpath[0];
} LeveldbCtx_t;


static ThreadLock_t * LeveldbCtxGetLock (LeveldbCtx dbctx)
{
return &(dbctx->ldbLock);
}


static void LeveldbCtxDestroy(LeveldbCtx dbctx, int dropdb)
{
ThreadLockAcquire(&dbctx->ldbLock, 1, 0);

/* SRW locks do not need to be explicitly destroyed. */
if (dropdb) {
// destroy leveldb directory
if (dbctx->err) {
leveldb_free(dbctx->err);
dbctx->err = NULL;
}

leveldb_destroy_db(dbctx->options, dbctx->dbpath, &dbctx->err);
if (dbctx->err) {
printf("leveldb_destroy_db error: %s.\n", dbctx->err);
}
}

if (dbctx->readoptions) {
leveldb_readoptions_destroy(dbctx->readoptions);
}

if (dbctx->writeoptions) {
leveldb_writeoptions_destroy(dbctx->writeoptions);
}

if (dbctx->options) {
leveldb_options_destroy(dbctx->options);
}

if (dbctx->err) {
leveldb_free(dbctx->err);
}

if (dbctx->cache) {
leveldb_cache_destroy(dbctx->cache);
}

if (dbctx->ldb) {
// close and free leveldb
leveldb_close(dbctx->ldb);
}

if (dbctx->env) {
leveldb_env_destroy(dbctx->env);
}

ThreadLockRelease(&dbctx->ldbLock, 1);
ThreadLockUninit(&dbctx->ldbLock);

free(dbctx);
}


static LDBCTX_RESULT LeveldbCtxCreate(const char *dbpath,
int accessmode,
int optionsflags,
size_t cachecapacity,
size_t writebuffersize,
size_t maxfilesize,
size_t blocksize,
int maxopenfiles,
int blockrestartinterval,
LeveldbCtx *pdbctx)
{
LeveldbCtx_t *dbctx = NULL;

int len = (dbpath ? (int) strnlen(dbpath, LDBCTX_DBPATH_MAX) : 0);
if (len == LDBCTX_DBPATH_MAX) {
return LDBCTX_ERROR_INVARG;
}
if (len == 0) {
return LDBCTX_ERROR_INVARG;
}

dbctx = (LeveldbCtx) calloc(1, sizeof(*dbctx) + len + 1);
if (! dbctx) {
return LDBCTX_ERROR_NOMEM;
}

memcpy(dbctx->dbpath, dbpath, len);
dbctx->pathlen = len;

dbctx->env = leveldb_create_default_env();
if (! dbctx->env) {
printf("leveldb_create_default_env failed.\n");
return LDBCTX_ERROR_LEVELDB;
}

dbctx->options = leveldb_options_create();
if (! dbctx->options) {
printf("leveldb_options_create failed.\n");
LeveldbCtxDestroy(dbctx, 0);
return LDBCTX_ERROR_LEVELDB;
}

leveldb_options_set_create_if_missing(dbctx->options, LDBCTX_OFLAG_CHECK(optionsflags, LDBCTX_OFLAG_CREATE_IF_MISSING));
leveldb_options_set_error_if_exists(dbctx->options, LDBCTX_OFLAG_CHECK(optionsflags, LDBCTX_OFLAG_ERROR_IF_EXISTS));
leveldb_options_set_paranoid_checks(dbctx->options, LDBCTX_OFLAG_CHECK(optionsflags, LDBCTX_OFLAG_PARANOID_CHECKS));

if (LDBCTX_OFLAG_CHECK(optionsflags, LDBCTX_OFLAG_SNAPPY_COMPRESSION)) {
leveldb_options_set_compression(dbctx->options, leveldb_snappy_compression);
} else {
leveldb_options_set_compression(dbctx->options, leveldb_no_compression);
}

if (writebuffersize) {
leveldb_options_set_write_buffer_size(dbctx->options, writebuffersize);
}
if (maxopenfiles) {
leveldb_options_set_max_open_files(dbctx->options, maxopenfiles);
}
if (blocksize) {
leveldb_options_set_block_size(dbctx->options, blocksize);
}
if (blockrestartinterval) {
leveldb_options_set_block_restart_interval(dbctx->options, blockrestartinterval);
}
if (maxfilesize) {
leveldb_options_set_max_file_size(dbctx->options, maxfilesize);
}

if (cachecapacity == (size_t)(-1)) {
dbctx->cachecapacity = LDBCTX_CACHE_CAPACITY_DEFAULT;
}
else {
dbctx->cachecapacity = cachecapacity;
}
if (dbctx->cachecapacity) {
dbctx->cache = leveldb_cache_create_lru(dbctx->cachecapacity);
if (!dbctx->cache) {
printf("leveldb_cache_create_lru failed.\n");
}
else {
leveldb_options_set_cache(dbctx->options, dbctx->cache);
}
}

if (accessmode & LDBCTX_ACCESSMODE_READ) {
dbctx->readoptions = leveldb_readoptions_create();
if (! dbctx->readoptions) {
printf("leveldb_readoptions_create failed.\n");
LeveldbCtxDestroy(dbctx, 0);
return LDBCTX_ERROR_LEVELDB;
}

leveldb_readoptions_set_verify_checksums(dbctx->readoptions, LDBCTX_OFLAG_CHECK(optionsflags, LDBCTX_OFLAG_READ_VERIFY_CHECKSUMS));
leveldb_readoptions_set_fill_cache(dbctx->readoptions, LDBCTX_OFLAG_CHECK(optionsflags, LDBCTX_OFLAG_READ_FILL_CACHE));
}

if (accessmode & LDBCTX_ACCESSMODE_WRITE) {
dbctx->writeoptions = leveldb_writeoptions_create();
if (! dbctx->writeoptions) {
printf("leveldb_writeoptions_create failed.\n");
LeveldbCtxDestroy(dbctx, 0);
return LDBCTX_ERROR_LEVELDB;
}

leveldb_writeoptions_set_sync(dbctx->writeoptions, LDBCTX_OFLAG_CHECK(optionsflags, LDBCTX_OFLAG_WRITE_SYNC));
}

dbctx->ldb = leveldb_open(dbctx->options, dbctx->dbpath, &dbctx->err);
if (! dbctx->ldb || dbctx->err) {
if (dbctx->err) {
printf("leveldb_open error: %s.\n", dbctx->err);
}
else {
printf("leveldb_open failed.\n");
}
LeveldbCtxDestroy(dbctx, 0);
return LDBCTX_ERROR_LEVELDB;
}

ThreadLockInit(&dbctx->ldbLock);

printf("leveldb-%d.%d\n", leveldb_major_version(), leveldb_minor_version());
*pdbctx = dbctx;
return LDBCTX_SUCCESS;
}

#if defined(__cplusplus)
}
#endif

#endif /* LEVELDB_C_WRAPPER_H_INCLUDED */