- Added a C binding 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 API. - Replaced SQLite's readseq benchmark to a more efficient version. SQLite readseq speeds increased by about a factor of 2x from the previous version. Also updated benchmark page to reflect readseq speed up. git-svn-id: https://leveldb.googlecode.com/svn/trunk@46 62dab493-f737-651d-591e-8d6aee1b9529main
parent
1bfbe76b4e
commit
021ee9c32b
@ -0,0 +1,453 @@ |
||||
// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
#include "leveldb/c.h" |
||||
|
||||
#include <stdlib.h> |
||||
#include <unistd.h> |
||||
#include "leveldb/cache.h" |
||||
#include "leveldb/comparator.h" |
||||
#include "leveldb/db.h" |
||||
#include "leveldb/env.h" |
||||
#include "leveldb/iterator.h" |
||||
#include "leveldb/options.h" |
||||
#include "leveldb/status.h" |
||||
#include "leveldb/write_batch.h" |
||||
|
||||
namespace leveldb { |
||||
|
||||
extern "C" { |
||||
|
||||
struct leveldb_t { DB* rep; }; |
||||
struct leveldb_iterator_t { Iterator* rep; }; |
||||
struct leveldb_writebatch_t { WriteBatch rep; }; |
||||
struct leveldb_snapshot_t { const Snapshot* rep; }; |
||||
struct leveldb_readoptions_t { ReadOptions rep; }; |
||||
struct leveldb_writeoptions_t { WriteOptions rep; }; |
||||
struct leveldb_options_t { Options rep; }; |
||||
struct leveldb_cache_t { Cache* rep; }; |
||||
struct leveldb_seqfile_t { SequentialFile* rep; }; |
||||
struct leveldb_randomfile_t { RandomAccessFile* rep; }; |
||||
struct leveldb_writablefile_t { WritableFile* rep; }; |
||||
struct leveldb_logger_t { Logger* rep; }; |
||||
struct leveldb_filelock_t { FileLock* rep; }; |
||||
|
||||
struct leveldb_comparator_t : public Comparator { |
||||
void* state_; |
||||
void (*destructor_)(void*); |
||||
int (*compare_)( |
||||
void*, |
||||
const char* a, size_t alen, |
||||
const char* b, size_t blen); |
||||
const char* (*name_)(void*); |
||||
|
||||
virtual ~leveldb_comparator_t() { |
||||
(*destructor_)(state_); |
||||
} |
||||
|
||||
virtual int Compare(const Slice& a, const Slice& b) const { |
||||
return (*compare_)(state_, a.data(), a.size(), b.data(), b.size()); |
||||
} |
||||
|
||||
virtual const char* Name() const { |
||||
return (*name_)(state_); |
||||
} |
||||
|
||||
// No-ops since the C binding does not support key shortening methods.
|
||||
virtual void FindShortestSeparator(std::string*, const Slice&) const { } |
||||
virtual void FindShortSuccessor(std::string* key) const { } |
||||
}; |
||||
|
||||
struct leveldb_env_t { |
||||
Env* rep; |
||||
bool is_default; |
||||
}; |
||||
|
||||
static bool SaveError(char** errptr, const Status& s) { |
||||
assert(errptr != NULL); |
||||
if (s.ok()) { |
||||
return false; |
||||
} else if (*errptr == NULL) { |
||||
*errptr = strdup(s.ToString().c_str()); |
||||
} else { |
||||
// TODO(sanjay): Merge with existing error?
|
||||
free(*errptr); |
||||
*errptr = strdup(s.ToString().c_str()); |
||||
} |
||||
return true; |
||||
} |
||||
|
||||
static char* CopyString(const std::string& str) { |
||||
char* result = reinterpret_cast<char*>(malloc(sizeof(char) * str.size())); |
||||
memcpy(result, str.data(), sizeof(char) * str.size()); |
||||
return result; |
||||
} |
||||
|
||||
leveldb_t* leveldb_open( |
||||
const leveldb_options_t* options, |
||||
const char* name, |
||||
char** errptr) { |
||||
DB* db; |
||||
if (SaveError(errptr, DB::Open(options->rep, std::string(name), &db))) { |
||||
return NULL; |
||||
} |
||||
leveldb_t* result = new leveldb_t; |
||||
result->rep = db; |
||||
return result; |
||||
} |
||||
|
||||
void leveldb_close(leveldb_t* db) { |
||||
delete db->rep; |
||||
delete db; |
||||
} |
||||
|
||||
void leveldb_put( |
||||
leveldb_t* db, |
||||
const leveldb_writeoptions_t* options, |
||||
const char* key, size_t keylen, |
||||
const char* val, size_t vallen, |
||||
char** errptr) { |
||||
SaveError(errptr, |
||||
db->rep->Put(options->rep, Slice(key, keylen), Slice(val, vallen))); |
||||
} |
||||
|
||||
void leveldb_delete( |
||||
leveldb_t* db, |
||||
const leveldb_writeoptions_t* options, |
||||
const char* key, size_t keylen, |
||||
char** errptr) { |
||||
SaveError(errptr, db->rep->Delete(options->rep, Slice(key, keylen))); |
||||
} |
||||
|
||||
|
||||
void leveldb_write( |
||||
leveldb_t* db, |
||||
const leveldb_writeoptions_t* options, |
||||
leveldb_writebatch_t* batch, |
||||
char** errptr) { |
||||
SaveError(errptr, db->rep->Write(options->rep, &batch->rep)); |
||||
} |
||||
|
||||
char* leveldb_get( |
||||
leveldb_t* db, |
||||
const leveldb_readoptions_t* options, |
||||
const char* key, size_t keylen, |
||||
size_t* vallen, |
||||
char** errptr) { |
||||
char* result = NULL; |
||||
std::string tmp; |
||||
Status s = db->rep->Get(options->rep, Slice(key, keylen), &tmp); |
||||
if (s.ok()) { |
||||
*vallen = tmp.size(); |
||||
result = CopyString(tmp); |
||||
} else { |
||||
*vallen = 0; |
||||
if (!s.IsNotFound()) { |
||||
SaveError(errptr, s); |
||||
} |
||||
} |
||||
return result; |
||||
} |
||||
|
||||
leveldb_iterator_t* leveldb_create_iterator( |
||||
leveldb_t* db, |
||||
const leveldb_readoptions_t* options) { |
||||
leveldb_iterator_t* result = new leveldb_iterator_t; |
||||
result->rep = db->rep->NewIterator(options->rep); |
||||
return result; |
||||
} |
||||
|
||||
const leveldb_snapshot_t* leveldb_create_snapshot( |
||||
leveldb_t* db) { |
||||
leveldb_snapshot_t* result = new leveldb_snapshot_t; |
||||
result->rep = db->rep->GetSnapshot(); |
||||
return result; |
||||
} |
||||
|
||||
void leveldb_release_snapshot( |
||||
leveldb_t* db, |
||||
const leveldb_snapshot_t* snapshot) { |
||||
db->rep->ReleaseSnapshot(snapshot->rep); |
||||
delete snapshot; |
||||
} |
||||
|
||||
const char* leveldb_property_value( |
||||
leveldb_t* db, |
||||
const char* propname) { |
||||
std::string tmp; |
||||
if (db->rep->GetProperty(Slice(propname), &tmp)) { |
||||
return CopyString(tmp); |
||||
} else { |
||||
return NULL; |
||||
} |
||||
} |
||||
|
||||
void leveldb_approximate_sizes( |
||||
leveldb_t* db, |
||||
int num_ranges, |
||||
const char* const* range_start_key, const size_t* range_start_key_len, |
||||
const char* const* range_limit_key, const size_t* range_limit_key_len, |
||||
uint64_t* sizes) { |
||||
Range* ranges = new Range[num_ranges]; |
||||
for (int i = 0; i < num_ranges; i++) { |
||||
ranges[i].start = Slice(range_start_key[i], range_start_key_len[i]); |
||||
ranges[i].limit = Slice(range_limit_key[i], range_limit_key_len[i]); |
||||
} |
||||
db->rep->GetApproximateSizes(ranges, num_ranges, sizes); |
||||
delete[] ranges; |
||||
} |
||||
|
||||
void leveldb_destroy_db( |
||||
const leveldb_options_t* options, |
||||
const char* name, |
||||
char** errptr) { |
||||
SaveError(errptr, DestroyDB(name, options->rep)); |
||||
} |
||||
|
||||
void leveldb_repair_db( |
||||
const leveldb_options_t* options, |
||||
const char* name, |
||||
char** errptr) { |
||||
SaveError(errptr, RepairDB(name, options->rep)); |
||||
} |
||||
|
||||
void leveldb_iter_destroy(leveldb_iterator_t* iter) { |
||||
delete iter->rep; |
||||
delete iter; |
||||
} |
||||
|
||||
unsigned char leveldb_iter_valid(const leveldb_iterator_t* iter) { |
||||
return iter->rep->Valid(); |
||||
} |
||||
|
||||
void leveldb_iter_seek_to_first(leveldb_iterator_t* iter) { |
||||
iter->rep->SeekToFirst(); |
||||
} |
||||
|
||||
void leveldb_iter_seek_to_last(leveldb_iterator_t* iter) { |
||||
iter->rep->SeekToLast(); |
||||
} |
||||
|
||||
void leveldb_iter_seek(leveldb_iterator_t* iter, const char* k, size_t klen) { |
||||
iter->rep->Seek(Slice(k, klen)); |
||||
} |
||||
|
||||
void leveldb_iter_next(leveldb_iterator_t* iter) { |
||||
iter->rep->Next(); |
||||
} |
||||
|
||||
void leveldb_iter_prev(leveldb_iterator_t* iter) { |
||||
iter->rep->Prev(); |
||||
} |
||||
|
||||
const char* leveldb_iter_key(const leveldb_iterator_t* iter, size_t* klen) { |
||||
Slice s = iter->rep->key(); |
||||
*klen = s.size(); |
||||
return s.data(); |
||||
} |
||||
|
||||
const char* leveldb_iter_value(const leveldb_iterator_t* iter, size_t* vlen) { |
||||
Slice s = iter->rep->value(); |
||||
*vlen = s.size(); |
||||
return s.data(); |
||||
} |
||||
|
||||
void leveldb_iter_get_error(const leveldb_iterator_t* iter, char** errptr) { |
||||
SaveError(errptr, iter->rep->status()); |
||||
} |
||||
|
||||
leveldb_writebatch_t* leveldb_writebatch_create() { |
||||
return new leveldb_writebatch_t; |
||||
} |
||||
|
||||
void leveldb_writebatch_destroy(leveldb_writebatch_t* b) { |
||||
delete b; |
||||
} |
||||
|
||||
void leveldb_writebatch_clear(leveldb_writebatch_t* b) { |
||||
b->rep.Clear(); |
||||
} |
||||
|
||||
void leveldb_writebatch_put( |
||||
leveldb_writebatch_t* b, |
||||
const char* key, size_t klen, |
||||
const char* val, size_t vlen) { |
||||
b->rep.Put(Slice(key, klen), Slice(val, vlen)); |
||||
} |
||||
|
||||
void leveldb_writebatch_delete( |
||||
leveldb_writebatch_t* b, |
||||
const char* key, size_t klen) { |
||||
b->rep.Delete(Slice(key, klen)); |
||||
} |
||||
|
||||
void leveldb_writebatch_iterate( |
||||
leveldb_writebatch_t* b, |
||||
void* state, |
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), |
||||
void (*deleted)(void*, const char* k, size_t klen)) { |
||||
class H : public WriteBatch::Handler { |
||||
public: |
||||
void* state_; |
||||
void (*put_)(void*, const char* k, size_t klen, const char* v, size_t vlen); |
||||
void (*deleted_)(void*, const char* k, size_t klen); |
||||
virtual void Put(const Slice& key, const Slice& value) { |
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size()); |
||||
} |
||||
virtual void Delete(const Slice& key) { |
||||
(*deleted_)(state_, key.data(), key.size()); |
||||
} |
||||
}; |
||||
H handler; |
||||
handler.state_ = state; |
||||
handler.put_ = put; |
||||
handler.deleted_ = deleted; |
||||
b->rep.Iterate(&handler); |
||||
} |
||||
|
||||
leveldb_options_t* leveldb_options_create() { |
||||
return new leveldb_options_t; |
||||
} |
||||
|
||||
void leveldb_options_destroy(leveldb_options_t* options) { |
||||
delete options; |
||||
} |
||||
|
||||
void leveldb_options_set_comparator( |
||||
leveldb_options_t* opt, |
||||
leveldb_comparator_t* cmp) { |
||||
opt->rep.comparator = cmp; |
||||
} |
||||
|
||||
void leveldb_options_set_create_if_missing( |
||||
leveldb_options_t* opt, unsigned char v) { |
||||
opt->rep.create_if_missing = v; |
||||
} |
||||
|
||||
void leveldb_options_set_error_if_exists( |
||||
leveldb_options_t* opt, unsigned char v) { |
||||
opt->rep.error_if_exists = v; |
||||
} |
||||
|
||||
void leveldb_options_set_paranoid_checks( |
||||
leveldb_options_t* opt, unsigned char v) { |
||||
opt->rep.paranoid_checks = v; |
||||
} |
||||
|
||||
void leveldb_options_set_env(leveldb_options_t* opt, leveldb_env_t* env) { |
||||
opt->rep.env = (env ? env->rep : NULL); |
||||
} |
||||
|
||||
void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) { |
||||
opt->rep.info_log = (l ? l->rep : NULL); |
||||
} |
||||
|
||||
void leveldb_options_set_write_buffer_size(leveldb_options_t* opt, size_t s) { |
||||
opt->rep.write_buffer_size = s; |
||||
} |
||||
|
||||
void leveldb_options_set_max_open_files(leveldb_options_t* opt, int n) { |
||||
opt->rep.max_open_files = n; |
||||
} |
||||
|
||||
void leveldb_options_set_cache(leveldb_options_t* opt, leveldb_cache_t* c) { |
||||
opt->rep.block_cache = c->rep; |
||||
} |
||||
|
||||
void leveldb_options_set_block_size(leveldb_options_t* opt, size_t s) { |
||||
opt->rep.block_size = s; |
||||
} |
||||
|
||||
void leveldb_options_set_block_restart_interval(leveldb_options_t* opt, int n) { |
||||
opt->rep.block_restart_interval = n; |
||||
} |
||||
|
||||
void leveldb_options_set_compression(leveldb_options_t* opt, int t) { |
||||
opt->rep.compression = static_cast<CompressionType>(t); |
||||
} |
||||
|
||||
leveldb_comparator_t* leveldb_comparator_create( |
||||
void* state, |
||||
void (*destructor)(void*), |
||||
int (*compare)( |
||||
void*, |
||||
const char* a, size_t alen, |
||||
const char* b, size_t blen), |
||||
const char* (*name)(void*)) { |
||||
leveldb_comparator_t* result = new leveldb_comparator_t; |
||||
result->state_ = state; |
||||
result->destructor_ = destructor; |
||||
result->compare_ = compare; |
||||
result->name_ = name; |
||||
return result; |
||||
} |
||||
|
||||
void leveldb_comparator_destroy(leveldb_comparator_t* cmp) { |
||||
delete cmp; |
||||
} |
||||
|
||||
leveldb_readoptions_t* leveldb_readoptions_create() { |
||||
return new leveldb_readoptions_t; |
||||
} |
||||
|
||||
void leveldb_readoptions_destroy(leveldb_readoptions_t* opt) { |
||||
delete opt; |
||||
} |
||||
|
||||
void leveldb_readoptions_set_verify_checksums( |
||||
leveldb_readoptions_t* opt, |
||||
unsigned char v) { |
||||
opt->rep.verify_checksums = v; |
||||
} |
||||
|
||||
void leveldb_readoptions_set_fill_cache( |
||||
leveldb_readoptions_t* opt, unsigned char v) { |
||||
opt->rep.fill_cache = v; |
||||
} |
||||
|
||||
void leveldb_readoptions_set_snapshot( |
||||
leveldb_readoptions_t* opt, |
||||
const leveldb_snapshot_t* snap) { |
||||
opt->rep.snapshot = (snap ? snap->rep : NULL); |
||||
} |
||||
|
||||
leveldb_writeoptions_t* leveldb_writeoptions_create() { |
||||
return new leveldb_writeoptions_t; |
||||
} |
||||
|
||||
void leveldb_writeoptions_destroy(leveldb_writeoptions_t* opt) { |
||||
delete opt; |
||||
} |
||||
|
||||
void leveldb_writeoptions_set_sync( |
||||
leveldb_writeoptions_t* opt, unsigned char v) { |
||||
opt->rep.sync = v; |
||||
} |
||||
|
||||
leveldb_cache_t* leveldb_cache_create_lru(size_t capacity) { |
||||
leveldb_cache_t* c = new leveldb_cache_t; |
||||
c->rep = NewLRUCache(capacity); |
||||
return c; |
||||
} |
||||
|
||||
void leveldb_cache_destroy(leveldb_cache_t* cache) { |
||||
delete cache->rep; |
||||
delete cache; |
||||
} |
||||
|
||||
leveldb_env_t* leveldb_create_default_env() { |
||||
leveldb_env_t* result = new leveldb_env_t; |
||||
result->rep = Env::Default(); |
||||
result->is_default = true; |
||||
return result; |
||||
} |
||||
|
||||
void leveldb_env_destroy(leveldb_env_t* env) { |
||||
if (!env->is_default) delete env->rep; |
||||
delete env; |
||||
} |
||||
|
||||
} // end extern "C"
|
||||
|
||||
} |
@ -0,0 +1,295 @@ |
||||
/* Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style license that can be |
||||
found in the LICENSE file. See the AUTHORS file for names of contributors. */ |
||||
|
||||
#include "leveldb/c.h" |
||||
|
||||
#include <stddef.h> |
||||
#include <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#include <sys/types.h> |
||||
#include <unistd.h> |
||||
|
||||
const char* phase = ""; |
||||
static char dbname[200]; |
||||
|
||||
static void StartPhase(const char* name) { |
||||
fprintf(stderr, "=== Test %s\n", name); |
||||
phase = name; |
||||
} |
||||
|
||||
#define CheckNoError(err) \ |
||||
if ((err) != NULL) { \
|
||||
fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, (err)); \
|
||||
abort(); \
|
||||
} |
||||
|
||||
#define CheckCondition(cond) \ |
||||
if (!(cond)) { \
|
||||
fprintf(stderr, "%s:%d: %s: %s\n", __FILE__, __LINE__, phase, #cond); \
|
||||
abort(); \
|
||||
} |
||||
|
||||
static void CheckEqual(const char* expected, const char* v, size_t n) { |
||||
if (expected == NULL && v == NULL) { |
||||
// ok
|
||||
} else if (expected != NULL && v != NULL && n == strlen(expected) && |
||||
memcmp(expected, v, n) == 0) { |
||||
// ok
|
||||
return; |
||||
} else { |
||||
fprintf(stderr, "%s: expected '%s', got '%s'\n", |
||||
phase, |
||||
(expected ? expected : "(null)"), |
||||
(v ? v : "(null")); |
||||
abort(); |
||||
} |
||||
} |
||||
|
||||
static void Free(char** ptr) { |
||||
if (*ptr) { |
||||
free(*ptr); |
||||
*ptr = NULL; |
||||
} |
||||
} |
||||
|
||||
static void CheckGet( |
||||
leveldb_t* db, |
||||
const leveldb_readoptions_t* options, |
||||
const char* key, |
||||
const char* expected) { |
||||
char* err = NULL; |
||||
size_t val_len; |
||||
char* val; |
||||
val = leveldb_get(db, options, key, strlen(key), &val_len, &err); |
||||
CheckNoError(err); |
||||
CheckEqual(expected, val, val_len); |
||||
Free(&val); |
||||
} |
||||
|
||||
static void CheckIter(leveldb_iterator_t* iter, |
||||
const char* key, const char* val) { |
||||
size_t len; |
||||
const char* str; |
||||
str = leveldb_iter_key(iter, &len); |
||||
CheckEqual(key, str, len); |
||||
str = leveldb_iter_value(iter, &len); |
||||
CheckEqual(val, str, len); |
||||
} |
||||
|
||||
// Callback from leveldb_writebatch_iterate()
|
||||
static void CheckPut(void* ptr, |
||||
const char* k, size_t klen, |
||||
const char* v, size_t vlen) { |
||||
int* state = (int*) ptr; |
||||
CheckCondition(*state < 2); |
||||
switch (*state) { |
||||
case 0: |
||||
CheckEqual("bar", k, klen); |
||||
CheckEqual("b", v, vlen); |
||||
break; |
||||
case 1: |
||||
CheckEqual("box", k, klen); |
||||
CheckEqual("c", v, vlen); |
||||
break; |
||||
} |
||||
(*state)++; |
||||
} |
||||
|
||||
// Callback from leveldb_writebatch_iterate()
|
||||
static void CheckDel(void* ptr, const char* k, size_t klen) { |
||||
int* state = (int*) ptr; |
||||
CheckCondition(*state == 2); |
||||
CheckEqual("bar", k, klen); |
||||
(*state)++; |
||||
} |
||||
|
||||
static void CmpDestroy(void* arg) { } |
||||
|
||||
static int CmpCompare(void* arg, const char* a, size_t alen, |
||||
const char* b, size_t blen) { |
||||
int n = (alen < blen) ? alen : blen; |
||||
int r = memcmp(a, b, n); |
||||
if (r == 0) { |
||||
if (alen < blen) r = -1; |
||||
else if (alen > blen) r = +1; |
||||
} |
||||
return r; |
||||
} |
||||
|
||||
static const char* CmpName(void* arg) { |
||||
return "foo"; |
||||
} |
||||
|
||||
int main(int argc, char** argv) { |
||||
leveldb_t* db; |
||||
leveldb_comparator_t* cmp; |
||||
leveldb_cache_t* cache; |
||||
leveldb_env_t* env; |
||||
leveldb_options_t* options; |
||||
leveldb_readoptions_t* roptions; |
||||
leveldb_writeoptions_t* woptions; |
||||
char* err = NULL; |
||||
|
||||
snprintf(dbname, sizeof(dbname), "/tmp/leveldb_c_test-%d", |
||||
((int) geteuid())); |
||||
|
||||
StartPhase("create_objects"); |
||||
cmp = leveldb_comparator_create(NULL, CmpDestroy, CmpCompare, CmpName); |
||||
env = leveldb_create_default_env(); |
||||
cache = leveldb_cache_create_lru(100000); |
||||
|
||||
options = leveldb_options_create(); |
||||
leveldb_options_set_comparator(options, cmp); |
||||
leveldb_options_set_error_if_exists(options, 1); |
||||
leveldb_options_set_cache(options, cache); |
||||
leveldb_options_set_env(options, env); |
||||
leveldb_options_set_info_log(options, NULL); |
||||
leveldb_options_set_write_buffer_size(options, 100000); |
||||
leveldb_options_set_paranoid_checks(options, 1); |
||||
leveldb_options_set_max_open_files(options, 10); |
||||
leveldb_options_set_block_size(options, 1024); |
||||
leveldb_options_set_block_restart_interval(options, 8); |
||||
leveldb_options_set_compression(options, leveldb_no_compression); |
||||
|
||||
roptions = leveldb_readoptions_create(); |
||||
leveldb_readoptions_set_verify_checksums(roptions, 1); |
||||
leveldb_readoptions_set_fill_cache(roptions, 0); |
||||
|
||||
woptions = leveldb_writeoptions_create(); |
||||
leveldb_writeoptions_set_sync(woptions, 1); |
||||
|
||||
StartPhase("destroy"); |
||||
leveldb_destroy_db(options, dbname, &err); |
||||
Free(&err); |
||||
|
||||
StartPhase("open_error"); |
||||
db = leveldb_open(options, dbname, &err); |
||||
CheckCondition(err != NULL); |
||||
Free(&err); |
||||
|
||||
StartPhase("open"); |
||||
leveldb_options_set_create_if_missing(options, 1); |
||||
db = leveldb_open(options, dbname, &err); |
||||
CheckNoError(err); |
||||
CheckGet(db, roptions, "foo", NULL); |
||||
|
||||
StartPhase("put"); |
||||
leveldb_put(db, woptions, "foo", 3, "hello", 5, &err); |
||||
CheckNoError(err); |
||||
CheckGet(db, roptions, "foo", "hello"); |
||||
|
||||
StartPhase("writebatch"); |
||||
{ |
||||
leveldb_writebatch_t* wb = leveldb_writebatch_create(); |
||||
leveldb_writebatch_put(wb, "foo", 3, "a", 1); |
||||
leveldb_writebatch_clear(wb); |
||||
leveldb_writebatch_put(wb, "bar", 3, "b", 1); |
||||
leveldb_writebatch_put(wb, "box", 3, "c", 1); |
||||
leveldb_writebatch_delete(wb, "bar", 3); |
||||
leveldb_write(db, woptions, wb, &err); |
||||
CheckNoError(err); |
||||
CheckGet(db, roptions, "foo", "hello"); |
||||
CheckGet(db, roptions, "bar", NULL); |
||||
CheckGet(db, roptions, "box", "c"); |
||||
int pos = 0; |
||||
leveldb_writebatch_iterate(wb, &pos, CheckPut, CheckDel); |
||||
CheckCondition(pos == 3); |
||||
leveldb_writebatch_destroy(wb); |
||||
} |
||||
|
||||
StartPhase("iter"); |
||||
{ |
||||
leveldb_iterator_t* iter = leveldb_create_iterator(db, roptions); |
||||
CheckCondition(!leveldb_iter_valid(iter)); |
||||
leveldb_iter_seek_to_first(iter); |
||||
CheckCondition(leveldb_iter_valid(iter)); |
||||
CheckIter(iter, "box", "c"); |
||||
leveldb_iter_next(iter); |
||||
CheckIter(iter, "foo", "hello"); |
||||
leveldb_iter_prev(iter); |
||||
CheckIter(iter, "box", "c"); |
||||
leveldb_iter_prev(iter); |
||||
CheckCondition(!leveldb_iter_valid(iter)); |
||||
leveldb_iter_seek_to_last(iter); |
||||
CheckIter(iter, "foo", "hello"); |
||||
leveldb_iter_seek(iter, "b", 1); |
||||
CheckIter(iter, "box", "c"); |
||||
leveldb_iter_get_error(iter, &err); |
||||
CheckNoError(err); |
||||
leveldb_iter_destroy(iter); |
||||
} |
||||
|
||||
StartPhase("approximate_sizes"); |
||||
{ |
||||
int i; |
||||
int n = 20000; |
||||
char keybuf[100]; |
||||
char valbuf[100]; |
||||
uint64_t sizes[2]; |
||||
const char* start[2] = { "a", "k00000000000000010000" }; |
||||
size_t start_len[2] = { 1, 21 }; |
||||
const char* limit[2] = { "k00000000000000010000", "z" }; |
||||
size_t limit_len[2] = { 21, 1 }; |
||||
leveldb_writeoptions_set_sync(woptions, 0); |
||||
for (i = 0; i < n; i++) { |
||||
snprintf(keybuf, sizeof(keybuf), "k%020d", i); |
||||
snprintf(valbuf, sizeof(valbuf), "v%020d", i); |
||||
leveldb_put(db, woptions, keybuf, strlen(keybuf), valbuf, strlen(valbuf), |
||||
&err); |
||||
CheckNoError(err); |
||||
} |
||||
leveldb_approximate_sizes(db, 2, start, start_len, limit, limit_len, sizes); |
||||
CheckCondition(sizes[0] > 0); |
||||
CheckCondition(sizes[1] > 0); |
||||
} |
||||
|
||||
StartPhase("property"); |
||||
{ |
||||
char* prop = leveldb_property_value(db, "nosuchprop"); |
||||
CheckCondition(prop == NULL); |
||||
prop = leveldb_property_value(db, "leveldb.stats"); |
||||
CheckCondition(prop != NULL); |
||||
Free(&prop); |
||||
} |
||||
|
||||
StartPhase("snapshot"); |
||||
{ |
||||
const leveldb_snapshot_t* snap; |
||||
snap = leveldb_create_snapshot(db); |
||||
leveldb_delete(db, woptions, "foo", 3, &err); |
||||
CheckNoError(err); |
||||
leveldb_readoptions_set_snapshot(roptions, snap); |
||||
CheckGet(db, roptions, "foo", "hello"); |
||||
leveldb_readoptions_set_snapshot(roptions, NULL); |
||||
CheckGet(db, roptions, "foo", NULL); |
||||
leveldb_release_snapshot(db, snap); |
||||
} |
||||
|
||||
StartPhase("repair"); |
||||
{ |
||||
leveldb_close(db); |
||||
leveldb_options_set_create_if_missing(options, 0); |
||||
leveldb_options_set_error_if_exists(options, 0); |
||||
leveldb_repair_db(options, dbname, &err); |
||||
CheckNoError(err); |
||||
db = leveldb_open(options, dbname, &err); |
||||
CheckNoError(err); |
||||
CheckGet(db, roptions, "foo", NULL); |
||||
CheckGet(db, roptions, "bar", NULL); |
||||
CheckGet(db, roptions, "box", "c"); |
||||
} |
||||
|
||||
StartPhase("cleanup"); |
||||
leveldb_close(db); |
||||
leveldb_options_destroy(options); |
||||
leveldb_readoptions_destroy(roptions); |
||||
leveldb_writeoptions_destroy(woptions); |
||||
leveldb_cache_destroy(cache); |
||||
leveldb_comparator_destroy(cmp); |
||||
leveldb_env_destroy(env); |
||||
|
||||
fprintf(stderr, "PASS\n"); |
||||
return 0; |
||||
} |
@ -0,0 +1,246 @@ |
||||
/* Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style license that can be |
||||
found in the LICENSE file. See the AUTHORS file for names of contributors. |
||||
|
||||
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 |
||||
a JNI api. |
||||
|
||||
Does not support: |
||||
. getters for the option types |
||||
. custom comparators that implement key shortening |
||||
. capturing post-write-snapshot |
||||
. custom iter, db, env, cache implementations using just the C bindings |
||||
|
||||
Some conventions: |
||||
|
||||
(1) We expose just opaque struct pointers and functions to clients. |
||||
This allows us to change internal representations without having to |
||||
recompile clients. |
||||
|
||||
(2) For simplicity, there is no equivalent to the Slice type. Instead, |
||||
the caller has to pass the pointer and length as separate |
||||
arguments. |
||||
|
||||
(3) Errors are represented by a null-terminated c string. NULL |
||||
means no error. All operations that can raise an error are passed |
||||
a "char** errptr" as the last argument. One of the following must |
||||
be true on entry: |
||||
*errptr == NULL |
||||
*errptr points to a malloc()ed null-terminated error message |
||||
On success, a leveldb routine leaves *errptr unchanged. |
||||
On failure, leveldb frees the old value of *errptr and |
||||
set *errptr to a malloc()ed error message. |
||||
|
||||
(4) Bools have the type unsigned char (0 == false; rest == true) |
||||
|
||||
(5) All of the pointer arguments must be non-NULL. |
||||
*/ |
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_C_H_ |
||||
#define STORAGE_LEVELDB_INCLUDE_C_H_ |
||||
|
||||
#ifdef __cplusplus |
||||
extern "C" { |
||||
#endif |
||||
|
||||
#include <stdarg.h> |
||||
#include <stddef.h> |
||||
#include <stdint.h> |
||||
|
||||
/* Exported types */ |
||||
|
||||
typedef struct leveldb_t leveldb_t; |
||||
typedef struct leveldb_cache_t leveldb_cache_t; |
||||
typedef struct leveldb_comparator_t leveldb_comparator_t; |
||||
typedef struct leveldb_env_t leveldb_env_t; |
||||
typedef struct leveldb_filelock_t leveldb_filelock_t; |
||||
typedef struct leveldb_iterator_t leveldb_iterator_t; |
||||
typedef struct leveldb_logger_t leveldb_logger_t; |
||||
typedef struct leveldb_options_t leveldb_options_t; |
||||
typedef struct leveldb_randomfile_t leveldb_randomfile_t; |
||||
typedef struct leveldb_readoptions_t leveldb_readoptions_t; |
||||
typedef struct leveldb_seqfile_t leveldb_seqfile_t; |
||||
typedef struct leveldb_snapshot_t leveldb_snapshot_t; |
||||
typedef struct leveldb_writablefile_t leveldb_writablefile_t; |
||||
typedef struct leveldb_writebatch_t leveldb_writebatch_t; |
||||
typedef struct leveldb_writeoptions_t leveldb_writeoptions_t; |
||||
|
||||
/* DB operations */ |
||||
|
||||
extern leveldb_t* leveldb_open( |
||||
const leveldb_options_t* options, |
||||
const char* name, |
||||
char** errptr); |
||||
|
||||
extern void leveldb_close(leveldb_t* db); |
||||
|
||||
extern void leveldb_put( |
||||
leveldb_t* db, |
||||
const leveldb_writeoptions_t* options, |
||||
const char* key, size_t keylen, |
||||
const char* val, size_t vallen, |
||||
char** errptr); |
||||
|
||||
extern void leveldb_delete( |
||||
leveldb_t* db, |
||||
const leveldb_writeoptions_t* options, |
||||
const char* key, size_t keylen, |
||||
char** errptr); |
||||
|
||||
extern void leveldb_write( |
||||
leveldb_t* db, |
||||
const leveldb_writeoptions_t* options, |
||||
leveldb_writebatch_t* batch, |
||||
char** errptr); |
||||
|
||||
/* Returns NULL if not found. A malloc()ed array otherwise.
|
||||
Stores the length of the array in *vallen. */ |
||||
extern char* leveldb_get( |
||||
leveldb_t* db, |
||||
const leveldb_readoptions_t* options, |
||||
const char* key, size_t keylen, |
||||
size_t* vallen, |
||||
char** errptr); |
||||
|
||||
extern leveldb_iterator_t* leveldb_create_iterator( |
||||
leveldb_t* db, |
||||
const leveldb_readoptions_t* options); |
||||
|
||||
extern const leveldb_snapshot_t* leveldb_create_snapshot( |
||||
leveldb_t* db); |
||||
|
||||
extern void leveldb_release_snapshot( |
||||
leveldb_t* db, |
||||
const leveldb_snapshot_t* snapshot); |
||||
|
||||
/* Returns NULL if property name is unknown.
|
||||
Else returns a pointer to a malloc()-ed null-terminated value. */ |
||||
extern char* leveldb_property_value( |
||||
leveldb_t* db, |
||||
const char* propname); |
||||
|
||||
extern void leveldb_approximate_sizes( |
||||
leveldb_t* db, |
||||
int num_ranges, |
||||
const char* const* range_start_key, const size_t* range_start_key_len, |
||||
const char* const* range_limit_key, const size_t* range_limit_key_len, |
||||
uint64_t* sizes); |
||||
|
||||
/* Management operations */ |
||||
|
||||
extern void leveldb_destroy_db( |
||||
const leveldb_options_t* options, |
||||
const char* name, |
||||
char** errptr); |
||||
|
||||
extern void leveldb_repair_db( |
||||
const leveldb_options_t* options, |
||||
const char* name, |
||||
char** errptr); |
||||
|
||||
/* Iterator */ |
||||
|
||||
extern void leveldb_iter_destroy(leveldb_iterator_t*); |
||||
extern unsigned char leveldb_iter_valid(const leveldb_iterator_t*); |
||||
extern void leveldb_iter_seek_to_first(leveldb_iterator_t*); |
||||
extern void leveldb_iter_seek_to_last(leveldb_iterator_t*); |
||||
extern void leveldb_iter_seek(leveldb_iterator_t*, const char* k, size_t klen); |
||||
extern void leveldb_iter_next(leveldb_iterator_t*); |
||||
extern void leveldb_iter_prev(leveldb_iterator_t*); |
||||
extern const char* leveldb_iter_key(const leveldb_iterator_t*, size_t* klen); |
||||
extern const char* leveldb_iter_value(const leveldb_iterator_t*, size_t* vlen); |
||||
extern void leveldb_iter_get_error(const leveldb_iterator_t*, char** errptr); |
||||
|
||||
/* Write batch */ |
||||
|
||||
extern leveldb_writebatch_t* leveldb_writebatch_create(); |
||||
extern void leveldb_writebatch_destroy(leveldb_writebatch_t*); |
||||
extern void leveldb_writebatch_clear(leveldb_writebatch_t*); |
||||
extern void leveldb_writebatch_put( |
||||
leveldb_writebatch_t*, |
||||
const char* key, size_t klen, |
||||
const char* val, size_t vlen); |
||||
extern void leveldb_writebatch_delete( |
||||
leveldb_writebatch_t*, |
||||
const char* key, size_t klen); |
||||
extern void leveldb_writebatch_iterate( |
||||
leveldb_writebatch_t*, |
||||
void* state, |
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen), |
||||
void (*deleted)(void*, const char* k, size_t klen)); |
||||
|
||||
/* Options */ |
||||
|
||||
extern leveldb_options_t* leveldb_options_create(); |
||||
extern void leveldb_options_destroy(leveldb_options_t*); |
||||
extern void leveldb_options_set_comparator( |
||||
leveldb_options_t*, |
||||
leveldb_comparator_t*); |
||||
extern void leveldb_options_set_create_if_missing( |
||||
leveldb_options_t*, unsigned char); |
||||
extern void leveldb_options_set_error_if_exists( |
||||
leveldb_options_t*, unsigned char); |
||||
extern void leveldb_options_set_paranoid_checks( |
||||
leveldb_options_t*, unsigned char); |
||||
extern void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*); |
||||
extern void leveldb_options_set_info_log(leveldb_options_t*, leveldb_logger_t*); |
||||
extern void leveldb_options_set_write_buffer_size(leveldb_options_t*, size_t); |
||||
extern void leveldb_options_set_max_open_files(leveldb_options_t*, int); |
||||
extern void leveldb_options_set_cache(leveldb_options_t*, leveldb_cache_t*); |
||||
extern void leveldb_options_set_block_size(leveldb_options_t*, size_t); |
||||
extern void leveldb_options_set_block_restart_interval(leveldb_options_t*, int); |
||||
|
||||
enum { |
||||
leveldb_no_compression = 0, |
||||
leveldb_snappy_compression = 1 |
||||
}; |
||||
extern void leveldb_options_set_compression(leveldb_options_t*, int); |
||||
|
||||
/* Comparator */ |
||||
|
||||
extern leveldb_comparator_t* leveldb_comparator_create( |
||||
void* state, |
||||
void (*destructor)(void*), |
||||
int (*compare)( |
||||
void*, |
||||
const char* a, size_t alen, |
||||
const char* b, size_t blen), |
||||
const char* (*name)(void*)); |
||||
extern void leveldb_comparator_destroy(leveldb_comparator_t*); |
||||
|
||||
/* Read options */ |
||||
|
||||
extern leveldb_readoptions_t* leveldb_readoptions_create(); |
||||
extern void leveldb_readoptions_destroy(leveldb_readoptions_t*); |
||||
extern void leveldb_readoptions_set_verify_checksums( |
||||
leveldb_readoptions_t*, |
||||
unsigned char); |
||||
extern void leveldb_readoptions_set_fill_cache( |
||||
leveldb_readoptions_t*, unsigned char); |
||||
extern void leveldb_readoptions_set_snapshot( |
||||
leveldb_readoptions_t*, |
||||
const leveldb_snapshot_t*); |
||||
|
||||
/* Write options */ |
||||
|
||||
extern leveldb_writeoptions_t* leveldb_writeoptions_create(); |
||||
extern void leveldb_writeoptions_destroy(leveldb_writeoptions_t*); |
||||
extern void leveldb_writeoptions_set_sync( |
||||
leveldb_writeoptions_t*, unsigned char); |
||||
|
||||
/* Cache */ |
||||
|
||||
extern leveldb_cache_t* leveldb_cache_create_lru(size_t capacity); |
||||
extern void leveldb_cache_destroy(leveldb_cache_t* cache); |
||||
|
||||
/* Env */ |
||||
|
||||
extern leveldb_env_t* leveldb_create_default_env(); |
||||
extern void leveldb_env_destroy(leveldb_env_t*); |
||||
|
||||
#ifdef __cplusplus |
||||
} /* end extern "C" */ |
||||
#endif |
||||
|
||||
#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */ |
Loading…
Reference in new issue