- 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-8d6aee1b9529master
@ -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_ */ |