- 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-8d6aee1b9529pull/1/head
@ -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_ */ |