在 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@qq.com
*
* 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://github.com/google/leveldb/blob/master/doc/index.md#concurrency
*
* @author Liang Zhang <350137278@qq.com>
* @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:
* https://www.cnblogs.com/pandang/p/7279306.html
*/
#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 */