Permalink
Cannot retrieve contributors at this time
Fetching contributors…
| /* 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; | |
| } | |
| static const char* GetTempDir(void) { | |
| const char* ret = getenv("TEST_TMPDIR"); | |
| if (ret == NULL || ret[0] == '\0') | |
| ret = "/tmp"; | |
| return ret; | |
| } | |
| #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"; | |
| } | |
| // Custom filter policy | |
| static unsigned char fake_filter_result = 1; | |
| static void FilterDestroy(void* arg) { } | |
| static const char* FilterName(void* arg) { | |
| return "TestFilter"; | |
| } | |
| static char* FilterCreate( | |
| void* arg, | |
| const char* const* key_array, const size_t* key_length_array, | |
| int num_keys, | |
| size_t* filter_length) { | |
| *filter_length = 4; | |
| char* result = malloc(4); | |
| memcpy(result, "fake", 4); | |
| return result; | |
| } | |
| unsigned char FilterKeyMatch( | |
| void* arg, | |
| const char* key, size_t length, | |
| const char* filter, size_t filter_length) { | |
| CheckCondition(filter_length == 4); | |
| CheckCondition(memcmp(filter, "fake", 4) == 0); | |
| return fake_filter_result; | |
| } | |
| 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; | |
| int run = -1; | |
| CheckCondition(leveldb_major_version() >= 1); | |
| CheckCondition(leveldb_minor_version() >= 1); | |
| snprintf(dbname, sizeof(dbname), | |
| "%s/leveldb_c_test-%d", | |
| GetTempDir(), | |
| ((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("leveldb_free"); | |
| db = leveldb_open(options, dbname, &err); | |
| CheckCondition(err != NULL); | |
| leveldb_free(err); | |
| err = NULL; | |
| 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("compactall"); | |
| leveldb_compact_range(db, NULL, 0, NULL, 0); | |
| CheckGet(db, roptions, "foo", "hello"); | |
| StartPhase("compactrange"); | |
| leveldb_compact_range(db, "a", 1, "z", 1); | |
| 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"); | |
| leveldb_options_set_create_if_missing(options, 1); | |
| leveldb_options_set_error_if_exists(options, 1); | |
| } | |
| StartPhase("filter"); | |
| for (run = 0; run < 2; run++) { | |
| // First run uses custom filter, second run uses bloom filter | |
| CheckNoError(err); | |
| leveldb_filterpolicy_t* policy; | |
| if (run == 0) { | |
| policy = leveldb_filterpolicy_create( | |
| NULL, FilterDestroy, FilterCreate, FilterKeyMatch, FilterName); | |
| } else { | |
| policy = leveldb_filterpolicy_create_bloom(10); | |
| } | |
| // Create new database | |
| leveldb_close(db); | |
| leveldb_destroy_db(options, dbname, &err); | |
| leveldb_options_set_filter_policy(options, policy); | |
| db = leveldb_open(options, dbname, &err); | |
| CheckNoError(err); | |
| leveldb_put(db, woptions, "foo", 3, "foovalue", 8, &err); | |
| CheckNoError(err); | |
| leveldb_put(db, woptions, "bar", 3, "barvalue", 8, &err); | |
| CheckNoError(err); | |
| leveldb_compact_range(db, NULL, 0, NULL, 0); | |
| fake_filter_result = 1; | |
| CheckGet(db, roptions, "foo", "foovalue"); | |
| CheckGet(db, roptions, "bar", "barvalue"); | |
| if (phase == 0) { | |
| // Must not find value when custom filter returns false | |
| fake_filter_result = 0; | |
| CheckGet(db, roptions, "foo", NULL); | |
| CheckGet(db, roptions, "bar", NULL); | |
| fake_filter_result = 1; | |
| CheckGet(db, roptions, "foo", "foovalue"); | |
| CheckGet(db, roptions, "bar", "barvalue"); | |
| } | |
| leveldb_options_set_filter_policy(options, NULL); | |
| leveldb_filterpolicy_destroy(policy); | |
| } | |
| 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; | |
| } |