当前位置: 首页 > 工具软件 > LevelDB JNI > 使用案例 >

一个leveldb C api的包装

严项明
2023-12-01

一个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@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 */

 

 类似资料: