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