leveldb-haskell 0.0.1 → 0.0.2
raw patch · 105 files changed
+23219/−114 lines, 105 filesdep −leveldb-haskelldep ~basesetup-changedPVP ok
version bump matches the API change (PVP)
Dependencies removed: leveldb-haskell
Dependency ranges changed: base
API changes (from Hackage documentation)
Files
- AUTHORS +2/−0
- Example.lhs +0/−86
- Readme.md +17/−5
- Setup.hs +1/−1
- cbits/leveldb/db/builder.cc +88/−0
- cbits/leveldb/db/builder.h +34/−0
- cbits/leveldb/db/c.cc +471/−0
- cbits/leveldb/db/corruption_test.cc +359/−0
- cbits/leveldb/db/db_bench.cc +877/−0
- cbits/leveldb/db/db_impl.cc +1373/−0
- cbits/leveldb/db/db_impl.h +192/−0
- cbits/leveldb/db/db_iter.cc +299/−0
- cbits/leveldb/db/db_iter.h +26/−0
- cbits/leveldb/db/db_test.cc +1711/−0
- cbits/leveldb/db/dbformat.cc +120/−0
- cbits/leveldb/db/dbformat.h +215/−0
- cbits/leveldb/db/dbformat_test.cc +112/−0
- cbits/leveldb/db/filename.cc +139/−0
- cbits/leveldb/db/filename.h +80/−0
- cbits/leveldb/db/filename_test.cc +122/−0
- cbits/leveldb/db/log_format.h +35/−0
- cbits/leveldb/db/log_reader.cc +259/−0
- cbits/leveldb/db/log_reader.h +108/−0
- cbits/leveldb/db/log_test.cc +500/−0
- cbits/leveldb/db/log_writer.cc +103/−0
- cbits/leveldb/db/log_writer.h +48/−0
- cbits/leveldb/db/memtable.cc +145/−0
- cbits/leveldb/db/memtable.h +91/−0
- cbits/leveldb/db/repair.cc +387/−0
- cbits/leveldb/db/skiplist.h +378/−0
- cbits/leveldb/db/skiplist_test.cc +378/−0
- cbits/leveldb/db/snapshot.h +66/−0
- cbits/leveldb/db/table_cache.cc +95/−0
- cbits/leveldb/db/table_cache.h +50/−0
- cbits/leveldb/db/version_edit.cc +266/−0
- cbits/leveldb/db/version_edit.h +107/−0
- cbits/leveldb/db/version_edit_test.cc +46/−0
- cbits/leveldb/db/version_set.cc +1397/−0
- cbits/leveldb/db/version_set.h +370/−0
- cbits/leveldb/db/version_set_test.cc +179/−0
- cbits/leveldb/db/write_batch.cc +138/−0
- cbits/leveldb/db/write_batch_internal.h +47/−0
- cbits/leveldb/db/write_batch_test.cc +89/−0
- cbits/leveldb/helpers/memenv/memenv.cc +374/−0
- cbits/leveldb/helpers/memenv/memenv.h +20/−0
- cbits/leveldb/helpers/memenv/memenv_test.cc +232/−0
- cbits/leveldb/port/atomic_pointer.h +144/−0
- cbits/leveldb/port/port.h +21/−0
- cbits/leveldb/port/port_android.cc +64/−0
- cbits/leveldb/port/port_android.h +156/−0
- cbits/leveldb/port/port_example.h +125/−0
- cbits/leveldb/port/port_posix.cc +50/−0
- cbits/leveldb/port/port_posix.h +121/−0
- cbits/leveldb/table/block.cc +263/−0
- cbits/leveldb/table/block.h +43/−0
- cbits/leveldb/table/block_builder.cc +109/−0
- cbits/leveldb/table/block_builder.h +57/−0
- cbits/leveldb/table/format.cc +135/−0
- cbits/leveldb/table/format.h +103/−0
- cbits/leveldb/table/iterator.cc +67/−0
- cbits/leveldb/table/iterator_wrapper.h +63/−0
- cbits/leveldb/table/merger.cc +197/−0
- cbits/leveldb/table/merger.h +26/−0
- cbits/leveldb/table/table.cc +175/−0
- cbits/leveldb/table/table_builder.cc +227/−0
- cbits/leveldb/table/table_test.cc +854/−0
- cbits/leveldb/table/two_level_iterator.cc +182/−0
- cbits/leveldb/table/two_level_iterator.h +34/−0
- cbits/leveldb/util/arena.cc +68/−0
- cbits/leveldb/util/arena.h +68/−0
- cbits/leveldb/util/arena_test.cc +68/−0
- cbits/leveldb/util/cache.cc +328/−0
- cbits/leveldb/util/cache_test.cc +186/−0
- cbits/leveldb/util/coding.cc +194/−0
- cbits/leveldb/util/coding.h +104/−0
- cbits/leveldb/util/coding_test.cc +173/−0
- cbits/leveldb/util/comparator.cc +76/−0
- cbits/leveldb/util/crc32c.cc +332/−0
- cbits/leveldb/util/crc32c.h +45/−0
- cbits/leveldb/util/crc32c_test.cc +72/−0
- cbits/leveldb/util/env.cc +96/−0
- cbits/leveldb/util/env_posix.cc +567/−0
- cbits/leveldb/util/env_test.cc +104/−0
- cbits/leveldb/util/hash.cc +45/−0
- cbits/leveldb/util/hash.h +19/−0
- cbits/leveldb/util/histogram.cc +139/−0
- cbits/leveldb/util/histogram.h +42/−0
- cbits/leveldb/util/logging.cc +81/−0
- cbits/leveldb/util/logging.h +47/−0
- cbits/leveldb/util/mutexlock.h +39/−0
- cbits/leveldb/util/options.cc +28/−0
- cbits/leveldb/util/posix_logger.h +98/−0
- cbits/leveldb/util/random.h +59/−0
- cbits/leveldb/util/status.cc +75/−0
- cbits/leveldb/util/testharness.cc +77/−0
- cbits/leveldb/util/testharness.h +138/−0
- cbits/leveldb/util/testutil.cc +51/−0
- cbits/leveldb/util/testutil.h +53/−0
- configure +4165/−4
- configure.ac +64/−0
- examples/features.hs +56/−0
- leveldb-haskell.buildinfo.in +2/−0
- leveldb-haskell.cabal +83/−18
- src/Database/LevelDB.hs +7/−0
- src/Database/LevelDB/Base.hsc +8/−0
+ AUTHORS view
@@ -0,0 +1,2 @@+Kim Altintop <kim.altintop@gmail.com>+Austin Seipp <mad.one@gmail.com>
− Example.lhs
@@ -1,86 +0,0 @@-Comprehensive walkthough of the functionality provided by this library.--> {-# LANGUAGE OverloadedStrings #-}-> module Main where->-> import Control.Monad-> import Data.ByteString.Char8 hiding (take)-> import Prelude hiding (putStrLn)-> import System.FilePath->-> import Database.LevelDB->-> import Debug.Trace->->-> main :: IO ()-> main =--Almost all operations on a leveldb database occur within the @withLevelDB@-bracket, to ensure proper resource de-/allocation.--> withLevelDB dbdir [ CreateIfMissing, CacheSize 2048 ] $ \db -> do--The basic operations are @put@, @get@ and @delete@, straightforwardly:--> put db [] "foo" "bar"-> get db [ FillCache ] "foo" >>= print-> delete db [] "foo"-> get db [ FillCache ] "foo" >>= print--Additionally, we can write several key/value pairs in a batch, which is-guaranteed to be atomic. Note the use of the @Sync@ option here: it tells-leveldb to flush the data to disk before returning. While syncing may have a-considerable performance impact, it is most useful with batch operations.--Also note that we're taking a snapshot of the database before issueing the-write.--> withSnapshot db $ \snap -> do-> write db [ Sync ] [ Put "a" "one"-> , Put "b" "two"-> , Put "c" "three" ]--Now, we can perform a snapshot read. As expected, this will output nothing,-since we took the snapshot before writing.--> dumpEntries db [ UseSnapshot snap, FillCache ]--Conversely, we should see the values just written in a "dirty" read:--> dumpEntries db [ FillCache ]---Let's inspect the state of our database, just for the fun of it.--> approximateSize db ("a", "z") >>= print-> getProperty db SSTables >>= printProperty "sstables"-> getProperty db Stats >>= printProperty "stats"-> getProperty db (NumFilesAtLevel 1) >>= printProperty "num files at level"--Similar to the batch write above, we can also use @write@ to delete key/value-pairs:--> write db [ Sync ] [ Del "a", Del "b", Del "c" ]-> dumpEntries db [ FillCache ]->-> where-> dbdir = "/" </> "tmp" </> "leveltest"->-> dumpEntries db opts =-> withIterator db opts $ \iter -> do-> iterFirst iter-> iterEntries iter print->-> iterEntries iter f = do-> valid <- iterValid iter-> when valid $ do-> key <- iterKey iter-> val <- iterValue iter-> _ <- f (key, val)-> _ <- iterNext iter-> iterEntries iter f->-> printProperty l p = do-> putStrLn l-> maybe (putStrLn "n/a") putStrLn $ p
Readme.md view
@@ -5,12 +5,24 @@ Prerequisites: -* GHC 7.* (http://www.haskell.org/ghc)-* Cabal (http://www.haskell.org/cabal)-* `make` and a working C++ compiler+* [GHC 7.*](http://www.haskell.org/ghc)+* [Cabal](http://www.haskell.org/cabal)+* Optional: [Snappy](http://code.google.com/p/snappy),+ if compression support is desired+* Optional: `autoconf` -`cabal install` will build and install the package, including the bundled-LevelDB sources.+To install the latest version from hackage:++```shell+$ cabal install leveldb-haskell+```++To install from checked-out source:++```shell+$ autoconf+$ cabal install+``` ## Notes
Setup.hs view
@@ -1,2 +1,2 @@ import Distribution.Simple-main = defaultMain+main = defaultMainWithHooks autoconfUserHooks
+ cbits/leveldb/db/builder.cc view
@@ -0,0 +1,88 @@+// 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 "db/builder.h"++#include "db/filename.h"+#include "db/dbformat.h"+#include "db/table_cache.h"+#include "db/version_edit.h"+#include "leveldb/db.h"+#include "leveldb/env.h"+#include "leveldb/iterator.h"++namespace leveldb {++Status BuildTable(const std::string& dbname,+ Env* env,+ const Options& options,+ TableCache* table_cache,+ Iterator* iter,+ FileMetaData* meta) {+ Status s;+ meta->file_size = 0;+ iter->SeekToFirst();++ std::string fname = TableFileName(dbname, meta->number);+ if (iter->Valid()) {+ WritableFile* file;+ s = env->NewWritableFile(fname, &file);+ if (!s.ok()) {+ return s;+ }++ TableBuilder* builder = new TableBuilder(options, file);+ meta->smallest.DecodeFrom(iter->key());+ for (; iter->Valid(); iter->Next()) {+ Slice key = iter->key();+ meta->largest.DecodeFrom(key);+ builder->Add(key, iter->value());+ }++ // Finish and check for builder errors+ if (s.ok()) {+ s = builder->Finish();+ if (s.ok()) {+ meta->file_size = builder->FileSize();+ assert(meta->file_size > 0);+ }+ } else {+ builder->Abandon();+ }+ delete builder;++ // Finish and check for file errors+ if (s.ok()) {+ s = file->Sync();+ }+ if (s.ok()) {+ s = file->Close();+ }+ delete file;+ file = NULL;++ if (s.ok()) {+ // Verify that the table is usable+ Iterator* it = table_cache->NewIterator(ReadOptions(),+ meta->number,+ meta->file_size);+ s = it->status();+ delete it;+ }+ }++ // Check for input iterator errors+ if (!iter->status().ok()) {+ s = iter->status();+ }++ if (s.ok() && meta->file_size > 0) {+ // Keep it+ } else {+ env->DeleteFile(fname);+ }+ return s;+}++} // namespace leveldb
+ cbits/leveldb/db/builder.h view
@@ -0,0 +1,34 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_BUILDER_H_+#define STORAGE_LEVELDB_DB_BUILDER_H_++#include "leveldb/status.h"++namespace leveldb {++struct Options;+struct FileMetaData;++class Env;+class Iterator;+class TableCache;+class VersionEdit;++// Build a Table file from the contents of *iter. The generated file+// will be named according to meta->number. On success, the rest of+// *meta will be filled with metadata about the generated table.+// If no data is present in *iter, meta->file_size will be set to+// zero, and no Table file will be produced.+extern Status BuildTable(const std::string& dbname,+ Env* env,+ const Options& options,+ TableCache* table_cache,+ Iterator* iter,+ FileMetaData* meta);++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_BUILDER_H_
+ cbits/leveldb/db/c.cc view
@@ -0,0 +1,471 @@+// 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"++using leveldb::Cache;+using leveldb::Comparator;+using leveldb::CompressionType;+using leveldb::DB;+using leveldb::Env;+using leveldb::FileLock;+using leveldb::Iterator;+using leveldb::Logger;+using leveldb::NewLRUCache;+using leveldb::Options;+using leveldb::RandomAccessFile;+using leveldb::Range;+using leveldb::ReadOptions;+using leveldb::SequentialFile;+using leveldb::Slice;+using leveldb::Snapshot;+using leveldb::Status;+using leveldb::WritableFile;+using leveldb::WriteBatch;+using leveldb::WriteOptions;++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;+}++char* leveldb_property_value(+ leveldb_t* db,+ const char* propname) {+ std::string tmp;+ if (db->rep->GetProperty(Slice(propname), &tmp)) {+ // We use strdup() since we expect human readable output.+ return strdup(tmp.c_str());+ } 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"
+ cbits/leveldb/db/corruption_test.cc view
@@ -0,0 +1,359 @@+// 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/db.h"++#include <errno.h>+#include <fcntl.h>+#include <sys/stat.h>+#include <sys/types.h>+#include "leveldb/cache.h"+#include "leveldb/env.h"+#include "leveldb/table.h"+#include "leveldb/write_batch.h"+#include "db/db_impl.h"+#include "db/filename.h"+#include "db/log_format.h"+#include "db/version_set.h"+#include "util/logging.h"+#include "util/testharness.h"+#include "util/testutil.h"++namespace leveldb {++static const int kValueSize = 1000;++class CorruptionTest {+ public:+ test::ErrorEnv env_;+ std::string dbname_;+ Cache* tiny_cache_;+ Options options_;+ DB* db_;++ CorruptionTest() {+ tiny_cache_ = NewLRUCache(100);+ options_.env = &env_;+ dbname_ = test::TmpDir() + "/db_test";+ DestroyDB(dbname_, options_);++ db_ = NULL;+ options_.create_if_missing = true;+ Reopen();+ options_.create_if_missing = false;+ }++ ~CorruptionTest() {+ delete db_;+ DestroyDB(dbname_, Options());+ delete tiny_cache_;+ }++ Status TryReopen(Options* options = NULL) {+ delete db_;+ db_ = NULL;+ Options opt = (options ? *options : options_);+ opt.env = &env_;+ opt.block_cache = tiny_cache_;+ return DB::Open(opt, dbname_, &db_);+ }++ void Reopen(Options* options = NULL) {+ ASSERT_OK(TryReopen(options));+ }++ void RepairDB() {+ delete db_;+ db_ = NULL;+ ASSERT_OK(::leveldb::RepairDB(dbname_, options_));+ }++ void Build(int n) {+ std::string key_space, value_space;+ WriteBatch batch;+ for (int i = 0; i < n; i++) {+ //if ((i % 100) == 0) fprintf(stderr, "@ %d of %d\n", i, n);+ Slice key = Key(i, &key_space);+ batch.Clear();+ batch.Put(key, Value(i, &value_space));+ ASSERT_OK(db_->Write(WriteOptions(), &batch));+ }+ }++ void Check(int min_expected, int max_expected) {+ int next_expected = 0;+ int missed = 0;+ int bad_keys = 0;+ int bad_values = 0;+ int correct = 0;+ std::string value_space;+ Iterator* iter = db_->NewIterator(ReadOptions());+ for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {+ uint64_t key;+ Slice in(iter->key());+ if (!ConsumeDecimalNumber(&in, &key) ||+ !in.empty() ||+ key < next_expected) {+ bad_keys++;+ continue;+ }+ missed += (key - next_expected);+ next_expected = key + 1;+ if (iter->value() != Value(key, &value_space)) {+ bad_values++;+ } else {+ correct++;+ }+ }+ delete iter;++ fprintf(stderr,+ "expected=%d..%d; got=%d; bad_keys=%d; bad_values=%d; missed=%d\n",+ min_expected, max_expected, correct, bad_keys, bad_values, missed);+ ASSERT_LE(min_expected, correct);+ ASSERT_GE(max_expected, correct);+ }++ void Corrupt(FileType filetype, int offset, int bytes_to_corrupt) {+ // Pick file to corrupt+ std::vector<std::string> filenames;+ ASSERT_OK(env_.GetChildren(dbname_, &filenames));+ uint64_t number;+ FileType type;+ std::string fname;+ int picked_number = -1;+ for (int i = 0; i < filenames.size(); i++) {+ if (ParseFileName(filenames[i], &number, &type) &&+ type == filetype &&+ int(number) > picked_number) { // Pick latest file+ fname = dbname_ + "/" + filenames[i];+ picked_number = number;+ }+ }+ ASSERT_TRUE(!fname.empty()) << filetype;++ struct stat sbuf;+ if (stat(fname.c_str(), &sbuf) != 0) {+ const char* msg = strerror(errno);+ ASSERT_TRUE(false) << fname << ": " << msg;+ }++ if (offset < 0) {+ // Relative to end of file; make it absolute+ if (-offset > sbuf.st_size) {+ offset = 0;+ } else {+ offset = sbuf.st_size + offset;+ }+ }+ if (offset > sbuf.st_size) {+ offset = sbuf.st_size;+ }+ if (offset + bytes_to_corrupt > sbuf.st_size) {+ bytes_to_corrupt = sbuf.st_size - offset;+ }++ // Do it+ std::string contents;+ Status s = ReadFileToString(Env::Default(), fname, &contents);+ ASSERT_TRUE(s.ok()) << s.ToString();+ for (int i = 0; i < bytes_to_corrupt; i++) {+ contents[i + offset] ^= 0x80;+ }+ s = WriteStringToFile(Env::Default(), contents, fname);+ ASSERT_TRUE(s.ok()) << s.ToString();+ }++ int Property(const std::string& name) {+ std::string property;+ int result;+ if (db_->GetProperty(name, &property) &&+ sscanf(property.c_str(), "%d", &result) == 1) {+ return result;+ } else {+ return -1;+ }+ }++ // Return the ith key+ Slice Key(int i, std::string* storage) {+ char buf[100];+ snprintf(buf, sizeof(buf), "%016d", i);+ storage->assign(buf, strlen(buf));+ return Slice(*storage);+ }++ // Return the value to associate with the specified key+ Slice Value(int k, std::string* storage) {+ Random r(k);+ return test::RandomString(&r, kValueSize, storage);+ }+};++TEST(CorruptionTest, Recovery) {+ Build(100);+ Check(100, 100);+ Corrupt(kLogFile, 19, 1); // WriteBatch tag for first record+ Corrupt(kLogFile, log::kBlockSize + 1000, 1); // Somewhere in second block+ Reopen();++ // The 64 records in the first two log blocks are completely lost.+ Check(36, 36);+}++TEST(CorruptionTest, RecoverWriteError) {+ env_.writable_file_error_ = true;+ Status s = TryReopen();+ ASSERT_TRUE(!s.ok());+}++TEST(CorruptionTest, NewFileErrorDuringWrite) {+ // Do enough writing to force minor compaction+ env_.writable_file_error_ = true;+ const int num = 3 + (Options().write_buffer_size / kValueSize);+ std::string value_storage;+ Status s;+ for (int i = 0; s.ok() && i < num; i++) {+ WriteBatch batch;+ batch.Put("a", Value(100, &value_storage));+ s = db_->Write(WriteOptions(), &batch);+ }+ ASSERT_TRUE(!s.ok());+ ASSERT_GE(env_.num_writable_file_errors_, 1);+ env_.writable_file_error_ = false;+ Reopen();+}++TEST(CorruptionTest, TableFile) {+ Build(100);+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);+ dbi->TEST_CompactMemTable();+ dbi->TEST_CompactRange(0, NULL, NULL);+ dbi->TEST_CompactRange(1, NULL, NULL);++ Corrupt(kTableFile, 100, 1);+ Check(99, 99);+}++TEST(CorruptionTest, TableFileIndexData) {+ Build(10000); // Enough to build multiple Tables+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);+ dbi->TEST_CompactMemTable();++ Corrupt(kTableFile, -2000, 500);+ Reopen();+ Check(5000, 9999);+}++TEST(CorruptionTest, MissingDescriptor) {+ Build(1000);+ RepairDB();+ Reopen();+ Check(1000, 1000);+}++TEST(CorruptionTest, SequenceNumberRecovery) {+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v3"));+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v4"));+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v5"));+ RepairDB();+ Reopen();+ std::string v;+ ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));+ ASSERT_EQ("v5", v);+ // Write something. If sequence number was not recovered properly,+ // it will be hidden by an earlier write.+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v6"));+ ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));+ ASSERT_EQ("v6", v);+ Reopen();+ ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));+ ASSERT_EQ("v6", v);+}++TEST(CorruptionTest, CorruptedDescriptor) {+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "hello"));+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);+ dbi->TEST_CompactMemTable();+ dbi->TEST_CompactRange(0, NULL, NULL);++ Corrupt(kDescriptorFile, 0, 1000);+ Status s = TryReopen();+ ASSERT_TRUE(!s.ok());++ RepairDB();+ Reopen();+ std::string v;+ ASSERT_OK(db_->Get(ReadOptions(), "foo", &v));+ ASSERT_EQ("hello", v);+}++TEST(CorruptionTest, CompactionInputError) {+ Build(10);+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);+ dbi->TEST_CompactMemTable();+ const int last = config::kMaxMemCompactLevel;+ ASSERT_EQ(1, Property("leveldb.num-files-at-level" + NumberToString(last)));++ Corrupt(kTableFile, 100, 1);+ Check(9, 9);++ // Force compactions by writing lots of values+ Build(10000);+ Check(10000, 10000);+}++TEST(CorruptionTest, CompactionInputErrorParanoid) {+ Options options;+ options.paranoid_checks = true;+ options.write_buffer_size = 1048576;+ Reopen(&options);+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);++ // Fill levels >= 1 so memtable compaction outputs to level 1+ for (int level = 1; level < config::kNumLevels; level++) {+ dbi->Put(WriteOptions(), "", "begin");+ dbi->Put(WriteOptions(), "~", "end");+ dbi->TEST_CompactMemTable();+ }++ Build(10);+ dbi->TEST_CompactMemTable();+ ASSERT_EQ(1, Property("leveldb.num-files-at-level0"));++ Corrupt(kTableFile, 100, 1);+ Check(9, 9);++ // Write must eventually fail because of corrupted table+ Status s;+ std::string tmp1, tmp2;+ for (int i = 0; i < 10000 && s.ok(); i++) {+ s = db_->Put(WriteOptions(), Key(i, &tmp1), Value(i, &tmp2));+ }+ ASSERT_TRUE(!s.ok()) << "write did not fail in corrupted paranoid db";+}++TEST(CorruptionTest, UnrelatedKeys) {+ Build(10);+ DBImpl* dbi = reinterpret_cast<DBImpl*>(db_);+ dbi->TEST_CompactMemTable();+ Corrupt(kTableFile, 100, 1);++ std::string tmp1, tmp2;+ ASSERT_OK(db_->Put(WriteOptions(), Key(1000, &tmp1), Value(1000, &tmp2)));+ std::string v;+ ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));+ ASSERT_EQ(Value(1000, &tmp2).ToString(), v);+ dbi->TEST_CompactMemTable();+ ASSERT_OK(db_->Get(ReadOptions(), Key(1000, &tmp1), &v));+ ASSERT_EQ(Value(1000, &tmp2).ToString(), v);+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/db/db_bench.cc view
@@ -0,0 +1,877 @@+// 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 <sys/types.h>+#include <stdio.h>+#include <stdlib.h>+#include "db/db_impl.h"+#include "db/version_set.h"+#include "leveldb/cache.h"+#include "leveldb/db.h"+#include "leveldb/env.h"+#include "leveldb/write_batch.h"+#include "port/port.h"+#include "util/crc32c.h"+#include "util/histogram.h"+#include "util/mutexlock.h"+#include "util/random.h"+#include "util/testutil.h"++// Comma-separated list of operations to run in the specified order+// Actual benchmarks:+// fillseq -- write N values in sequential key order in async mode+// fillrandom -- write N values in random key order in async mode+// overwrite -- overwrite N values in random key order in async mode+// fillsync -- write N/100 values in random key order in sync mode+// fill100K -- write N/1000 100K values in random order in async mode+// readseq -- read N times sequentially+// readreverse -- read N times in reverse order+// readrandom -- read N times in random order+// readhot -- read N times in random order from 1% section of DB+// crc32c -- repeated crc32c of 4K of data+// acquireload -- load N*1000 times+// Meta operations:+// compact -- Compact the entire DB+// stats -- Print DB stats+// heapprofile -- Dump a heap profile (if supported by this port)+static const char* FLAGS_benchmarks =+ "fillseq,"+ "fillsync,"+ "fillrandom,"+ "overwrite,"+ "readrandom,"+ "readrandom," // Extra run to allow previous compactions to quiesce+ "readseq,"+ "readreverse,"+ "compact,"+ "readrandom,"+ "readseq,"+ "readreverse,"+ "fill100K,"+ "crc32c,"+ "snappycomp,"+ "snappyuncomp,"+ "acquireload,"+ ;++// Number of key/values to place in database+static int FLAGS_num = 1000000;++// Number of read operations to do. If negative, do FLAGS_num reads.+static int FLAGS_reads = -1;++// Number of concurrent threads to run.+static int FLAGS_threads = 1;++// Size of each value+static int FLAGS_value_size = 100;++// Arrange to generate values that shrink to this fraction of+// their original size after compression+static double FLAGS_compression_ratio = 0.5;++// Print histogram of operation timings+static bool FLAGS_histogram = false;++// Number of bytes to buffer in memtable before compacting+// (initialized to default value by "main")+static int FLAGS_write_buffer_size = 0;++// Number of bytes to use as a cache of uncompressed data.+// Negative means use default settings.+static int FLAGS_cache_size = -1;++// Maximum number of files to keep open at the same time (use default if == 0)+static int FLAGS_open_files = 0;++// If true, do not destroy the existing database. If you set this+// flag and also specify a benchmark that wants a fresh database, that+// benchmark will fail.+static bool FLAGS_use_existing_db = false;++// Use the db with the following name.+static const char* FLAGS_db = "/tmp/dbbench";++namespace leveldb {++namespace {++// Helper for quickly generating random data.+class RandomGenerator {+ private:+ std::string data_;+ int pos_;++ public:+ RandomGenerator() {+ // We use a limited amount of data over and over again and ensure+ // that it is larger than the compression window (32KB), and also+ // large enough to serve all typical value sizes we want to write.+ Random rnd(301);+ std::string piece;+ while (data_.size() < 1048576) {+ // Add a short fragment that is as compressible as specified+ // by FLAGS_compression_ratio.+ test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece);+ data_.append(piece);+ }+ pos_ = 0;+ }++ Slice Generate(int len) {+ if (pos_ + len > data_.size()) {+ pos_ = 0;+ assert(len < data_.size());+ }+ pos_ += len;+ return Slice(data_.data() + pos_ - len, len);+ }+};++static Slice TrimSpace(Slice s) {+ int start = 0;+ while (start < s.size() && isspace(s[start])) {+ start++;+ }+ int limit = s.size();+ while (limit > start && isspace(s[limit-1])) {+ limit--;+ }+ return Slice(s.data() + start, limit - start);+}++static void AppendWithSpace(std::string* str, Slice msg) {+ if (msg.empty()) return;+ if (!str->empty()) {+ str->push_back(' ');+ }+ str->append(msg.data(), msg.size());+}++class Stats {+ private:+ double start_;+ double finish_;+ double seconds_;+ int done_;+ int next_report_;+ int64_t bytes_;+ double last_op_finish_;+ Histogram hist_;+ std::string message_;++ public:+ Stats() { Start(); }++ void Start() {+ next_report_ = 100;+ last_op_finish_ = start_;+ hist_.Clear();+ done_ = 0;+ bytes_ = 0;+ seconds_ = 0;+ start_ = Env::Default()->NowMicros();+ finish_ = start_;+ message_.clear();+ }++ void Merge(const Stats& other) {+ hist_.Merge(other.hist_);+ done_ += other.done_;+ bytes_ += other.bytes_;+ seconds_ += other.seconds_;+ if (other.start_ < start_) start_ = other.start_;+ if (other.finish_ > finish_) finish_ = other.finish_;++ // Just keep the messages from one thread+ if (message_.empty()) message_ = other.message_;+ }++ void Stop() {+ finish_ = Env::Default()->NowMicros();+ seconds_ = (finish_ - start_) * 1e-6;+ }++ void AddMessage(Slice msg) {+ AppendWithSpace(&message_, msg);+ }++ void FinishedSingleOp() {+ if (FLAGS_histogram) {+ double now = Env::Default()->NowMicros();+ double micros = now - last_op_finish_;+ hist_.Add(micros);+ if (micros > 20000) {+ fprintf(stderr, "long op: %.1f micros%30s\r", micros, "");+ fflush(stderr);+ }+ last_op_finish_ = now;+ }++ done_++;+ if (done_ >= next_report_) {+ if (next_report_ < 1000) next_report_ += 100;+ else if (next_report_ < 5000) next_report_ += 500;+ else if (next_report_ < 10000) next_report_ += 1000;+ else if (next_report_ < 50000) next_report_ += 5000;+ else if (next_report_ < 100000) next_report_ += 10000;+ else if (next_report_ < 500000) next_report_ += 50000;+ else next_report_ += 100000;+ fprintf(stderr, "... finished %d ops%30s\r", done_, "");+ fflush(stderr);+ }+ }++ void AddBytes(int64_t n) {+ bytes_ += n;+ }++ void Report(const Slice& name) {+ // Pretend at least one op was done in case we are running a benchmark+ // that does not call FinishedSingleOp().+ if (done_ < 1) done_ = 1;++ std::string extra;+ if (bytes_ > 0) {+ // Rate is computed on actual elapsed time, not the sum of per-thread+ // elapsed times.+ double elapsed = (finish_ - start_) * 1e-6;+ char rate[100];+ snprintf(rate, sizeof(rate), "%6.1f MB/s",+ (bytes_ / 1048576.0) / elapsed);+ extra = rate;+ }+ AppendWithSpace(&extra, message_);++ fprintf(stdout, "%-12s : %11.3f micros/op;%s%s\n",+ name.ToString().c_str(),+ seconds_ * 1e6 / done_,+ (extra.empty() ? "" : " "),+ extra.c_str());+ if (FLAGS_histogram) {+ fprintf(stdout, "Microseconds per op:\n%s\n", hist_.ToString().c_str());+ }+ fflush(stdout);+ }+};++// State shared by all concurrent executions of the same benchmark.+struct SharedState {+ port::Mutex mu;+ port::CondVar cv;+ int total;++ // Each thread goes through the following states:+ // (1) initializing+ // (2) waiting for others to be initialized+ // (3) running+ // (4) done++ int num_initialized;+ int num_done;+ bool start;++ SharedState() : cv(&mu) { }+};++// Per-thread state for concurrent executions of the same benchmark.+struct ThreadState {+ int tid; // 0..n-1 when running in n threads+ Random rand; // Has different seeds for different threads+ Stats stats;+ SharedState* shared;++ ThreadState(int index)+ : tid(index),+ rand(1000 + index) {+ }+};++} // namespace++class Benchmark {+ private:+ Cache* cache_;+ DB* db_;+ int num_;+ int value_size_;+ int entries_per_batch_;+ WriteOptions write_options_;+ int reads_;+ int heap_counter_;++ void PrintHeader() {+ const int kKeySize = 16;+ PrintEnvironment();+ fprintf(stdout, "Keys: %d bytes each\n", kKeySize);+ fprintf(stdout, "Values: %d bytes each (%d bytes after compression)\n",+ FLAGS_value_size,+ static_cast<int>(FLAGS_value_size * FLAGS_compression_ratio + 0.5));+ fprintf(stdout, "Entries: %d\n", num_);+ fprintf(stdout, "RawSize: %.1f MB (estimated)\n",+ ((static_cast<int64_t>(kKeySize + FLAGS_value_size) * num_)+ / 1048576.0));+ fprintf(stdout, "FileSize: %.1f MB (estimated)\n",+ (((kKeySize + FLAGS_value_size * FLAGS_compression_ratio) * num_)+ / 1048576.0));+ PrintWarnings();+ fprintf(stdout, "------------------------------------------------\n");+ }++ void PrintWarnings() {+#if defined(__GNUC__) && !defined(__OPTIMIZE__)+ fprintf(stdout,+ "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"+ );+#endif+#ifndef NDEBUG+ fprintf(stdout,+ "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n");+#endif++ // See if snappy is working by attempting to compress a compressible string+ const char text[] = "yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy";+ std::string compressed;+ if (!port::Snappy_Compress(text, sizeof(text), &compressed)) {+ fprintf(stdout, "WARNING: Snappy compression is not enabled\n");+ } else if (compressed.size() >= sizeof(text)) {+ fprintf(stdout, "WARNING: Snappy compression is not effective\n");+ }+ }++ void PrintEnvironment() {+ fprintf(stderr, "LevelDB: version %d.%d\n",+ kMajorVersion, kMinorVersion);++#if defined(__linux)+ time_t now = time(NULL);+ fprintf(stderr, "Date: %s", ctime(&now)); // ctime() adds newline++ FILE* cpuinfo = fopen("/proc/cpuinfo", "r");+ if (cpuinfo != NULL) {+ char line[1000];+ int num_cpus = 0;+ std::string cpu_type;+ std::string cache_size;+ while (fgets(line, sizeof(line), cpuinfo) != NULL) {+ const char* sep = strchr(line, ':');+ if (sep == NULL) {+ continue;+ }+ Slice key = TrimSpace(Slice(line, sep - 1 - line));+ Slice val = TrimSpace(Slice(sep + 1));+ if (key == "model name") {+ ++num_cpus;+ cpu_type = val.ToString();+ } else if (key == "cache size") {+ cache_size = val.ToString();+ }+ }+ fclose(cpuinfo);+ fprintf(stderr, "CPU: %d * %s\n", num_cpus, cpu_type.c_str());+ fprintf(stderr, "CPUCache: %s\n", cache_size.c_str());+ }+#endif+ }++ public:+ Benchmark()+ : cache_(FLAGS_cache_size >= 0 ? NewLRUCache(FLAGS_cache_size) : NULL),+ db_(NULL),+ num_(FLAGS_num),+ value_size_(FLAGS_value_size),+ entries_per_batch_(1),+ reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads),+ heap_counter_(0) {+ std::vector<std::string> files;+ Env::Default()->GetChildren(FLAGS_db, &files);+ for (int i = 0; i < files.size(); i++) {+ if (Slice(files[i]).starts_with("heap-")) {+ Env::Default()->DeleteFile(std::string(FLAGS_db) + "/" + files[i]);+ }+ }+ if (!FLAGS_use_existing_db) {+ DestroyDB(FLAGS_db, Options());+ }+ }++ ~Benchmark() {+ delete db_;+ delete cache_;+ }++ void Run() {+ PrintHeader();+ Open();++ const char* benchmarks = FLAGS_benchmarks;+ while (benchmarks != NULL) {+ const char* sep = strchr(benchmarks, ',');+ Slice name;+ if (sep == NULL) {+ name = benchmarks;+ benchmarks = NULL;+ } else {+ name = Slice(benchmarks, sep - benchmarks);+ benchmarks = sep + 1;+ }++ // Reset parameters that may be overriddden bwlow+ num_ = FLAGS_num;+ reads_ = (FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads);+ value_size_ = FLAGS_value_size;+ entries_per_batch_ = 1;+ write_options_ = WriteOptions();++ void (Benchmark::*method)(ThreadState*) = NULL;+ bool fresh_db = false;+ int num_threads = FLAGS_threads;++ if (name == Slice("fillseq")) {+ fresh_db = true;+ method = &Benchmark::WriteSeq;+ } else if (name == Slice("fillbatch")) {+ fresh_db = true;+ entries_per_batch_ = 1000;+ method = &Benchmark::WriteSeq;+ } else if (name == Slice("fillrandom")) {+ fresh_db = true;+ method = &Benchmark::WriteRandom;+ } else if (name == Slice("overwrite")) {+ fresh_db = false;+ method = &Benchmark::WriteRandom;+ } else if (name == Slice("fillsync")) {+ fresh_db = true;+ num_ /= 1000;+ write_options_.sync = true;+ method = &Benchmark::WriteRandom;+ } else if (name == Slice("fill100K")) {+ fresh_db = true;+ num_ /= 1000;+ value_size_ = 100 * 1000;+ method = &Benchmark::WriteRandom;+ } else if (name == Slice("readseq")) {+ method = &Benchmark::ReadSequential;+ } else if (name == Slice("readreverse")) {+ method = &Benchmark::ReadReverse;+ } else if (name == Slice("readrandom")) {+ method = &Benchmark::ReadRandom;+ } else if (name == Slice("readhot")) {+ method = &Benchmark::ReadHot;+ } else if (name == Slice("readrandomsmall")) {+ reads_ /= 1000;+ method = &Benchmark::ReadRandom;+ } else if (name == Slice("readwhilewriting")) {+ num_threads++; // Add extra thread for writing+ method = &Benchmark::ReadWhileWriting;+ } else if (name == Slice("compact")) {+ method = &Benchmark::Compact;+ } else if (name == Slice("crc32c")) {+ method = &Benchmark::Crc32c;+ } else if (name == Slice("acquireload")) {+ method = &Benchmark::AcquireLoad;+ } else if (name == Slice("snappycomp")) {+ method = &Benchmark::SnappyCompress;+ } else if (name == Slice("snappyuncomp")) {+ method = &Benchmark::SnappyUncompress;+ } else if (name == Slice("heapprofile")) {+ HeapProfile();+ } else if (name == Slice("stats")) {+ PrintStats();+ } else {+ if (name != Slice()) { // No error message for empty name+ fprintf(stderr, "unknown benchmark '%s'\n", name.ToString().c_str());+ }+ }++ if (fresh_db) {+ if (FLAGS_use_existing_db) {+ fprintf(stdout, "%-12s : skipped (--use_existing_db is true)\n",+ name.ToString().c_str());+ method = NULL;+ } else {+ delete db_;+ db_ = NULL;+ DestroyDB(FLAGS_db, Options());+ Open();+ }+ }++ if (method != NULL) {+ RunBenchmark(num_threads, name, method);+ }+ }+ }++ private:+ struct ThreadArg {+ Benchmark* bm;+ SharedState* shared;+ ThreadState* thread;+ void (Benchmark::*method)(ThreadState*);+ };++ static void ThreadBody(void* v) {+ ThreadArg* arg = reinterpret_cast<ThreadArg*>(v);+ SharedState* shared = arg->shared;+ ThreadState* thread = arg->thread;+ {+ MutexLock l(&shared->mu);+ shared->num_initialized++;+ if (shared->num_initialized >= shared->total) {+ shared->cv.SignalAll();+ }+ while (!shared->start) {+ shared->cv.Wait();+ }+ }++ thread->stats.Start();+ (arg->bm->*(arg->method))(thread);+ thread->stats.Stop();++ {+ MutexLock l(&shared->mu);+ shared->num_done++;+ if (shared->num_done >= shared->total) {+ shared->cv.SignalAll();+ }+ }+ }++ void RunBenchmark(int n, Slice name,+ void (Benchmark::*method)(ThreadState*)) {+ SharedState shared;+ shared.total = n;+ shared.num_initialized = 0;+ shared.num_done = 0;+ shared.start = false;++ ThreadArg* arg = new ThreadArg[n];+ for (int i = 0; i < n; i++) {+ arg[i].bm = this;+ arg[i].method = method;+ arg[i].shared = &shared;+ arg[i].thread = new ThreadState(i);+ arg[i].thread->shared = &shared;+ Env::Default()->StartThread(ThreadBody, &arg[i]);+ }++ shared.mu.Lock();+ while (shared.num_initialized < n) {+ shared.cv.Wait();+ }++ shared.start = true;+ shared.cv.SignalAll();+ while (shared.num_done < n) {+ shared.cv.Wait();+ }+ shared.mu.Unlock();++ for (int i = 1; i < n; i++) {+ arg[0].thread->stats.Merge(arg[i].thread->stats);+ }+ arg[0].thread->stats.Report(name);++ for (int i = 0; i < n; i++) {+ delete arg[i].thread;+ }+ delete[] arg;+ }++ void Crc32c(ThreadState* thread) {+ // Checksum about 500MB of data total+ const int size = 4096;+ const char* label = "(4K per op)";+ std::string data(size, 'x');+ int64_t bytes = 0;+ uint32_t crc = 0;+ while (bytes < 500 * 1048576) {+ crc = crc32c::Value(data.data(), size);+ thread->stats.FinishedSingleOp();+ bytes += size;+ }+ // Print so result is not dead+ fprintf(stderr, "... crc=0x%x\r", static_cast<unsigned int>(crc));++ thread->stats.AddBytes(bytes);+ thread->stats.AddMessage(label);+ }++ void AcquireLoad(ThreadState* thread) {+ int dummy;+ port::AtomicPointer ap(&dummy);+ int count = 0;+ void *ptr = NULL;+ thread->stats.AddMessage("(each op is 1000 loads)");+ while (count < 100000) {+ for (int i = 0; i < 1000; i++) {+ ptr = ap.Acquire_Load();+ }+ count++;+ thread->stats.FinishedSingleOp();+ }+ if (ptr == NULL) exit(1); // Disable unused variable warning.+ }++ void SnappyCompress(ThreadState* thread) {+ RandomGenerator gen;+ Slice input = gen.Generate(Options().block_size);+ int64_t bytes = 0;+ int64_t produced = 0;+ bool ok = true;+ std::string compressed;+ while (ok && bytes < 1024 * 1048576) { // Compress 1G+ ok = port::Snappy_Compress(input.data(), input.size(), &compressed);+ produced += compressed.size();+ bytes += input.size();+ thread->stats.FinishedSingleOp();+ }++ if (!ok) {+ thread->stats.AddMessage("(snappy failure)");+ } else {+ char buf[100];+ snprintf(buf, sizeof(buf), "(output: %.1f%%)",+ (produced * 100.0) / bytes);+ thread->stats.AddMessage(buf);+ thread->stats.AddBytes(bytes);+ }+ }++ void SnappyUncompress(ThreadState* thread) {+ RandomGenerator gen;+ Slice input = gen.Generate(Options().block_size);+ std::string compressed;+ bool ok = port::Snappy_Compress(input.data(), input.size(), &compressed);+ int64_t bytes = 0;+ char* uncompressed = new char[input.size()];+ while (ok && bytes < 1024 * 1048576) { // Compress 1G+ ok = port::Snappy_Uncompress(compressed.data(), compressed.size(),+ uncompressed);+ bytes += input.size();+ thread->stats.FinishedSingleOp();+ }+ delete[] uncompressed;++ if (!ok) {+ thread->stats.AddMessage("(snappy failure)");+ } else {+ thread->stats.AddBytes(bytes);+ }+ }++ void Open() {+ assert(db_ == NULL);+ Options options;+ options.create_if_missing = !FLAGS_use_existing_db;+ options.block_cache = cache_;+ options.write_buffer_size = FLAGS_write_buffer_size;+ Status s = DB::Open(options, FLAGS_db, &db_);+ if (!s.ok()) {+ fprintf(stderr, "open error: %s\n", s.ToString().c_str());+ exit(1);+ }+ }++ void WriteSeq(ThreadState* thread) {+ DoWrite(thread, true);+ }++ void WriteRandom(ThreadState* thread) {+ DoWrite(thread, false);+ }++ void DoWrite(ThreadState* thread, bool seq) {+ if (num_ != FLAGS_num) {+ char msg[100];+ snprintf(msg, sizeof(msg), "(%d ops)", num_);+ thread->stats.AddMessage(msg);+ }++ RandomGenerator gen;+ WriteBatch batch;+ Status s;+ int64_t bytes = 0;+ for (int i = 0; i < num_; i += entries_per_batch_) {+ batch.Clear();+ for (int j = 0; j < entries_per_batch_; j++) {+ const int k = seq ? i+j : (thread->rand.Next() % FLAGS_num);+ char key[100];+ snprintf(key, sizeof(key), "%016d", k);+ batch.Put(key, gen.Generate(value_size_));+ bytes += value_size_ + strlen(key);+ thread->stats.FinishedSingleOp();+ }+ s = db_->Write(write_options_, &batch);+ if (!s.ok()) {+ fprintf(stderr, "put error: %s\n", s.ToString().c_str());+ exit(1);+ }+ }+ thread->stats.AddBytes(bytes);+ }++ void ReadSequential(ThreadState* thread) {+ Iterator* iter = db_->NewIterator(ReadOptions());+ int i = 0;+ int64_t bytes = 0;+ for (iter->SeekToFirst(); i < reads_ && iter->Valid(); iter->Next()) {+ bytes += iter->key().size() + iter->value().size();+ thread->stats.FinishedSingleOp();+ ++i;+ }+ delete iter;+ thread->stats.AddBytes(bytes);+ }++ void ReadReverse(ThreadState* thread) {+ Iterator* iter = db_->NewIterator(ReadOptions());+ int i = 0;+ int64_t bytes = 0;+ for (iter->SeekToLast(); i < reads_ && iter->Valid(); iter->Prev()) {+ bytes += iter->key().size() + iter->value().size();+ thread->stats.FinishedSingleOp();+ ++i;+ }+ delete iter;+ thread->stats.AddBytes(bytes);+ }++ void ReadRandom(ThreadState* thread) {+ ReadOptions options;+ std::string value;+ for (int i = 0; i < reads_; i++) {+ char key[100];+ const int k = thread->rand.Next() % FLAGS_num;+ snprintf(key, sizeof(key), "%016d", k);+ db_->Get(options, key, &value);+ thread->stats.FinishedSingleOp();+ }+ }++ void ReadHot(ThreadState* thread) {+ ReadOptions options;+ std::string value;+ const int range = (FLAGS_num + 99) / 100;+ for (int i = 0; i < reads_; i++) {+ char key[100];+ const int k = thread->rand.Next() % range;+ snprintf(key, sizeof(key), "%016d", k);+ db_->Get(options, key, &value);+ thread->stats.FinishedSingleOp();+ }+ }++ void ReadWhileWriting(ThreadState* thread) {+ if (thread->tid > 0) {+ ReadRandom(thread);+ } else {+ // Special thread that keeps writing until other threads are done.+ RandomGenerator gen;+ while (true) {+ {+ MutexLock l(&thread->shared->mu);+ if (thread->shared->num_done + 1 >= thread->shared->num_initialized) {+ // Other threads have finished+ break;+ }+ }++ const int k = thread->rand.Next() % FLAGS_num;+ char key[100];+ snprintf(key, sizeof(key), "%016d", k);+ Status s = db_->Put(write_options_, key, gen.Generate(value_size_));+ if (!s.ok()) {+ fprintf(stderr, "put error: %s\n", s.ToString().c_str());+ exit(1);+ }+ }++ // Do not count any of the preceding work/delay in stats.+ thread->stats.Start();+ }+ }++ void Compact(ThreadState* thread) {+ db_->CompactRange(NULL, NULL);+ }++ void PrintStats() {+ std::string stats;+ if (!db_->GetProperty("leveldb.stats", &stats)) {+ stats = "(failed)";+ }+ fprintf(stdout, "\n%s\n", stats.c_str());+ }++ static void WriteToFile(void* arg, const char* buf, int n) {+ reinterpret_cast<WritableFile*>(arg)->Append(Slice(buf, n));+ }++ void HeapProfile() {+ char fname[100];+ snprintf(fname, sizeof(fname), "%s/heap-%04d", FLAGS_db, ++heap_counter_);+ WritableFile* file;+ Status s = Env::Default()->NewWritableFile(fname, &file);+ if (!s.ok()) {+ fprintf(stderr, "%s\n", s.ToString().c_str());+ return;+ }+ bool ok = port::GetHeapProfile(WriteToFile, file);+ delete file;+ if (!ok) {+ fprintf(stderr, "heap profiling not supported\n");+ Env::Default()->DeleteFile(fname);+ }+ }+};++} // namespace leveldb++int main(int argc, char** argv) {+ FLAGS_write_buffer_size = leveldb::Options().write_buffer_size;+ FLAGS_open_files = leveldb::Options().max_open_files;++ for (int i = 1; i < argc; i++) {+ double d;+ int n;+ char junk;+ if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) {+ FLAGS_benchmarks = argv[i] + strlen("--benchmarks=");+ } else if (sscanf(argv[i], "--compression_ratio=%lf%c", &d, &junk) == 1) {+ FLAGS_compression_ratio = d;+ } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 &&+ (n == 0 || n == 1)) {+ FLAGS_histogram = n;+ } else if (sscanf(argv[i], "--use_existing_db=%d%c", &n, &junk) == 1 &&+ (n == 0 || n == 1)) {+ FLAGS_use_existing_db = n;+ } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) {+ FLAGS_num = n;+ } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) {+ FLAGS_reads = n;+ } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) {+ FLAGS_threads = n;+ } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) {+ FLAGS_value_size = n;+ } else if (sscanf(argv[i], "--write_buffer_size=%d%c", &n, &junk) == 1) {+ FLAGS_write_buffer_size = n;+ } else if (sscanf(argv[i], "--cache_size=%d%c", &n, &junk) == 1) {+ FLAGS_cache_size = n;+ } else if (sscanf(argv[i], "--open_files=%d%c", &n, &junk) == 1) {+ FLAGS_open_files = n;+ } else if (strncmp(argv[i], "--db=", 5) == 0) {+ FLAGS_db = argv[i] + 5;+ } else {+ fprintf(stderr, "Invalid flag '%s'\n", argv[i]);+ exit(1);+ }+ }++ leveldb::Benchmark benchmark;+ benchmark.Run();+ return 0;+}
+ cbits/leveldb/db/db_impl.cc view
@@ -0,0 +1,1373 @@+// 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 "db/db_impl.h"++#include <algorithm>+#include <set>+#include <string>+#include <stdint.h>+#include <stdio.h>+#include <vector>+#include "db/builder.h"+#include "db/db_iter.h"+#include "db/dbformat.h"+#include "db/filename.h"+#include "db/log_reader.h"+#include "db/log_writer.h"+#include "db/memtable.h"+#include "db/table_cache.h"+#include "db/version_set.h"+#include "db/write_batch_internal.h"+#include "leveldb/db.h"+#include "leveldb/env.h"+#include "leveldb/status.h"+#include "leveldb/table.h"+#include "leveldb/table_builder.h"+#include "port/port.h"+#include "table/block.h"+#include "table/merger.h"+#include "table/two_level_iterator.h"+#include "util/coding.h"+#include "util/logging.h"+#include "util/mutexlock.h"++namespace leveldb {++struct DBImpl::CompactionState {+ Compaction* const compaction;++ // Sequence numbers < smallest_snapshot are not significant since we+ // will never have to service a snapshot below smallest_snapshot.+ // Therefore if we have seen a sequence number S <= smallest_snapshot,+ // we can drop all entries for the same key with sequence numbers < S.+ SequenceNumber smallest_snapshot;++ // Files produced by compaction+ struct Output {+ uint64_t number;+ uint64_t file_size;+ InternalKey smallest, largest;+ };+ std::vector<Output> outputs;++ // State kept for output being generated+ WritableFile* outfile;+ TableBuilder* builder;++ uint64_t total_bytes;++ Output* current_output() { return &outputs[outputs.size()-1]; }++ explicit CompactionState(Compaction* c)+ : compaction(c),+ outfile(NULL),+ builder(NULL),+ total_bytes(0) {+ }+};++// Fix user-supplied options to be reasonable+template <class T,class V>+static void ClipToRange(T* ptr, V minvalue, V maxvalue) {+ if (static_cast<V>(*ptr) > maxvalue) *ptr = maxvalue;+ if (static_cast<V>(*ptr) < minvalue) *ptr = minvalue;+}+Options SanitizeOptions(const std::string& dbname,+ const InternalKeyComparator* icmp,+ const Options& src) {+ Options result = src;+ result.comparator = icmp;+ ClipToRange(&result.max_open_files, 20, 50000);+ ClipToRange(&result.write_buffer_size, 64<<10, 1<<30);+ ClipToRange(&result.block_size, 1<<10, 4<<20);+ if (result.info_log == NULL) {+ // Open a log file in the same directory as the db+ src.env->CreateDir(dbname); // In case it does not exist+ src.env->RenameFile(InfoLogFileName(dbname), OldInfoLogFileName(dbname));+ Status s = src.env->NewLogger(InfoLogFileName(dbname), &result.info_log);+ if (!s.ok()) {+ // No place suitable for logging+ result.info_log = NULL;+ }+ }+ if (result.block_cache == NULL) {+ result.block_cache = NewLRUCache(8 << 20);+ }+ return result;+}++DBImpl::DBImpl(const Options& options, const std::string& dbname)+ : env_(options.env),+ internal_comparator_(options.comparator),+ options_(SanitizeOptions(dbname, &internal_comparator_, options)),+ owns_info_log_(options_.info_log != options.info_log),+ owns_cache_(options_.block_cache != options.block_cache),+ dbname_(dbname),+ db_lock_(NULL),+ shutting_down_(NULL),+ bg_cv_(&mutex_),+ mem_(new MemTable(internal_comparator_)),+ imm_(NULL),+ logfile_(NULL),+ logfile_number_(0),+ log_(NULL),+ logger_(NULL),+ logger_cv_(&mutex_),+ bg_compaction_scheduled_(false),+ manual_compaction_(NULL) {+ mem_->Ref();+ has_imm_.Release_Store(NULL);++ // Reserve ten files or so for other uses and give the rest to TableCache.+ const int table_cache_size = options.max_open_files - 10;+ table_cache_ = new TableCache(dbname_, &options_, table_cache_size);++ versions_ = new VersionSet(dbname_, &options_, table_cache_,+ &internal_comparator_);+}++DBImpl::~DBImpl() {+ // Wait for background work to finish+ mutex_.Lock();+ shutting_down_.Release_Store(this); // Any non-NULL value is ok+ while (bg_compaction_scheduled_) {+ bg_cv_.Wait();+ }+ mutex_.Unlock();++ if (db_lock_ != NULL) {+ env_->UnlockFile(db_lock_);+ }++ delete versions_;+ if (mem_ != NULL) mem_->Unref();+ if (imm_ != NULL) imm_->Unref();+ delete log_;+ delete logfile_;+ delete table_cache_;++ if (owns_info_log_) {+ delete options_.info_log;+ }+ if (owns_cache_) {+ delete options_.block_cache;+ }+}++Status DBImpl::NewDB() {+ VersionEdit new_db;+ new_db.SetComparatorName(user_comparator()->Name());+ new_db.SetLogNumber(0);+ new_db.SetNextFile(2);+ new_db.SetLastSequence(0);++ const std::string manifest = DescriptorFileName(dbname_, 1);+ WritableFile* file;+ Status s = env_->NewWritableFile(manifest, &file);+ if (!s.ok()) {+ return s;+ }+ {+ log::Writer log(file);+ std::string record;+ new_db.EncodeTo(&record);+ s = log.AddRecord(record);+ if (s.ok()) {+ s = file->Close();+ }+ }+ delete file;+ if (s.ok()) {+ // Make "CURRENT" file that points to the new manifest file.+ s = SetCurrentFile(env_, dbname_, 1);+ } else {+ env_->DeleteFile(manifest);+ }+ return s;+}++void DBImpl::MaybeIgnoreError(Status* s) const {+ if (s->ok() || options_.paranoid_checks) {+ // No change needed+ } else {+ Log(options_.info_log, "Ignoring error %s", s->ToString().c_str());+ *s = Status::OK();+ }+}++void DBImpl::DeleteObsoleteFiles() {+ // Make a set of all of the live files+ std::set<uint64_t> live = pending_outputs_;+ versions_->AddLiveFiles(&live);++ std::vector<std::string> filenames;+ env_->GetChildren(dbname_, &filenames); // Ignoring errors on purpose+ uint64_t number;+ FileType type;+ for (size_t i = 0; i < filenames.size(); i++) {+ if (ParseFileName(filenames[i], &number, &type)) {+ bool keep = true;+ switch (type) {+ case kLogFile:+ keep = ((number >= versions_->LogNumber()) ||+ (number == versions_->PrevLogNumber()));+ break;+ case kDescriptorFile:+ // Keep my manifest file, and any newer incarnations'+ // (in case there is a race that allows other incarnations)+ keep = (number >= versions_->ManifestFileNumber());+ break;+ case kTableFile:+ keep = (live.find(number) != live.end());+ break;+ case kTempFile:+ // Any temp files that are currently being written to must+ // be recorded in pending_outputs_, which is inserted into "live"+ keep = (live.find(number) != live.end());+ break;+ case kCurrentFile:+ case kDBLockFile:+ case kInfoLogFile:+ keep = true;+ break;+ }++ if (!keep) {+ if (type == kTableFile) {+ table_cache_->Evict(number);+ }+ Log(options_.info_log, "Delete type=%d #%lld\n",+ int(type),+ static_cast<unsigned long long>(number));+ env_->DeleteFile(dbname_ + "/" + filenames[i]);+ }+ }+ }+}++Status DBImpl::Recover(VersionEdit* edit) {+ mutex_.AssertHeld();++ // Ignore error from CreateDir since the creation of the DB is+ // committed only when the descriptor is created, and this directory+ // may already exist from a previous failed creation attempt.+ env_->CreateDir(dbname_);+ assert(db_lock_ == NULL);+ Status s = env_->LockFile(LockFileName(dbname_), &db_lock_);+ if (!s.ok()) {+ return s;+ }++ if (!env_->FileExists(CurrentFileName(dbname_))) {+ if (options_.create_if_missing) {+ s = NewDB();+ if (!s.ok()) {+ return s;+ }+ } else {+ return Status::InvalidArgument(+ dbname_, "does not exist (create_if_missing is false)");+ }+ } else {+ if (options_.error_if_exists) {+ return Status::InvalidArgument(+ dbname_, "exists (error_if_exists is true)");+ }+ }++ s = versions_->Recover();+ if (s.ok()) {+ SequenceNumber max_sequence(0);++ // Recover from all newer log files than the ones named in the+ // descriptor (new log files may have been added by the previous+ // incarnation without registering them in the descriptor).+ //+ // Note that PrevLogNumber() is no longer used, but we pay+ // attention to it in case we are recovering a database+ // produced by an older version of leveldb.+ const uint64_t min_log = versions_->LogNumber();+ const uint64_t prev_log = versions_->PrevLogNumber();+ std::vector<std::string> filenames;+ s = env_->GetChildren(dbname_, &filenames);+ if (!s.ok()) {+ return s;+ }+ uint64_t number;+ FileType type;+ std::vector<uint64_t> logs;+ for (size_t i = 0; i < filenames.size(); i++) {+ if (ParseFileName(filenames[i], &number, &type)+ && type == kLogFile+ && ((number >= min_log) || (number == prev_log))) {+ logs.push_back(number);+ }+ }++ // Recover in the order in which the logs were generated+ std::sort(logs.begin(), logs.end());+ for (size_t i = 0; i < logs.size(); i++) {+ s = RecoverLogFile(logs[i], edit, &max_sequence);++ // The previous incarnation may not have written any MANIFEST+ // records after allocating this log number. So we manually+ // update the file number allocation counter in VersionSet.+ versions_->MarkFileNumberUsed(logs[i]);+ }++ if (s.ok()) {+ if (versions_->LastSequence() < max_sequence) {+ versions_->SetLastSequence(max_sequence);+ }+ }+ }++ return s;+}++Status DBImpl::RecoverLogFile(uint64_t log_number,+ VersionEdit* edit,+ SequenceNumber* max_sequence) {+ struct LogReporter : public log::Reader::Reporter {+ Env* env;+ Logger* info_log;+ const char* fname;+ Status* status; // NULL if options_.paranoid_checks==false+ virtual void Corruption(size_t bytes, const Status& s) {+ Log(info_log, "%s%s: dropping %d bytes; %s",+ (this->status == NULL ? "(ignoring error) " : ""),+ fname, static_cast<int>(bytes), s.ToString().c_str());+ if (this->status != NULL && this->status->ok()) *this->status = s;+ }+ };++ mutex_.AssertHeld();++ // Open the log file+ std::string fname = LogFileName(dbname_, log_number);+ SequentialFile* file;+ Status status = env_->NewSequentialFile(fname, &file);+ if (!status.ok()) {+ MaybeIgnoreError(&status);+ return status;+ }++ // Create the log reader.+ LogReporter reporter;+ reporter.env = env_;+ reporter.info_log = options_.info_log;+ reporter.fname = fname.c_str();+ reporter.status = (options_.paranoid_checks ? &status : NULL);+ // We intentially make log::Reader do checksumming even if+ // paranoid_checks==false so that corruptions cause entire commits+ // to be skipped instead of propagating bad information (like overly+ // large sequence numbers).+ log::Reader reader(file, &reporter, true/*checksum*/,+ 0/*initial_offset*/);+ Log(options_.info_log, "Recovering log #%llu",+ (unsigned long long) log_number);++ // Read all the records and add to a memtable+ std::string scratch;+ Slice record;+ WriteBatch batch;+ MemTable* mem = NULL;+ while (reader.ReadRecord(&record, &scratch) &&+ status.ok()) {+ if (record.size() < 12) {+ reporter.Corruption(+ record.size(), Status::Corruption("log record too small"));+ continue;+ }+ WriteBatchInternal::SetContents(&batch, record);++ if (mem == NULL) {+ mem = new MemTable(internal_comparator_);+ mem->Ref();+ }+ status = WriteBatchInternal::InsertInto(&batch, mem);+ MaybeIgnoreError(&status);+ if (!status.ok()) {+ break;+ }+ const SequenceNumber last_seq =+ WriteBatchInternal::Sequence(&batch) ++ WriteBatchInternal::Count(&batch) - 1;+ if (last_seq > *max_sequence) {+ *max_sequence = last_seq;+ }++ if (mem->ApproximateMemoryUsage() > options_.write_buffer_size) {+ status = WriteLevel0Table(mem, edit, NULL);+ if (!status.ok()) {+ // Reflect errors immediately so that conditions like full+ // file-systems cause the DB::Open() to fail.+ break;+ }+ mem->Unref();+ mem = NULL;+ }+ }++ if (status.ok() && mem != NULL) {+ status = WriteLevel0Table(mem, edit, NULL);+ // Reflect errors immediately so that conditions like full+ // file-systems cause the DB::Open() to fail.+ }++ if (mem != NULL) mem->Unref();+ delete file;+ return status;+}++Status DBImpl::WriteLevel0Table(MemTable* mem, VersionEdit* edit,+ Version* base) {+ mutex_.AssertHeld();+ const uint64_t start_micros = env_->NowMicros();+ FileMetaData meta;+ meta.number = versions_->NewFileNumber();+ pending_outputs_.insert(meta.number);+ Iterator* iter = mem->NewIterator();+ Log(options_.info_log, "Level-0 table #%llu: started",+ (unsigned long long) meta.number);++ Status s;+ {+ mutex_.Unlock();+ s = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta);+ mutex_.Lock();+ }++ Log(options_.info_log, "Level-0 table #%llu: %lld bytes %s",+ (unsigned long long) meta.number,+ (unsigned long long) meta.file_size,+ s.ToString().c_str());+ delete iter;+ pending_outputs_.erase(meta.number);+++ // Note that if file_size is zero, the file has been deleted and+ // should not be added to the manifest.+ int level = 0;+ if (s.ok() && meta.file_size > 0) {+ const Slice min_user_key = meta.smallest.user_key();+ const Slice max_user_key = meta.largest.user_key();+ if (base != NULL) {+ level = base->PickLevelForMemTableOutput(min_user_key, max_user_key);+ }+ edit->AddFile(level, meta.number, meta.file_size,+ meta.smallest, meta.largest);+ }++ CompactionStats stats;+ stats.micros = env_->NowMicros() - start_micros;+ stats.bytes_written = meta.file_size;+ stats_[level].Add(stats);+ return s;+}++Status DBImpl::CompactMemTable() {+ mutex_.AssertHeld();+ assert(imm_ != NULL);++ // Save the contents of the memtable as a new Table+ VersionEdit edit;+ Version* base = versions_->current();+ base->Ref();+ Status s = WriteLevel0Table(imm_, &edit, base);+ base->Unref();++ if (s.ok() && shutting_down_.Acquire_Load()) {+ s = Status::IOError("Deleting DB during memtable compaction");+ }++ // Replace immutable memtable with the generated Table+ if (s.ok()) {+ edit.SetPrevLogNumber(0);+ edit.SetLogNumber(logfile_number_); // Earlier logs no longer needed+ s = versions_->LogAndApply(&edit, &mutex_);+ }++ if (s.ok()) {+ // Commit to the new state+ imm_->Unref();+ imm_ = NULL;+ has_imm_.Release_Store(NULL);+ DeleteObsoleteFiles();+ }++ return s;+}++void DBImpl::CompactRange(const Slice* begin, const Slice* end) {+ int max_level_with_files = 1;+ {+ MutexLock l(&mutex_);+ Version* base = versions_->current();+ for (int level = 1; level < config::kNumLevels; level++) {+ if (base->OverlapInLevel(level, begin, end)) {+ max_level_with_files = level;+ }+ }+ }+ TEST_CompactMemTable(); // TODO(sanjay): Skip if memtable does not overlap+ for (int level = 0; level < max_level_with_files; level++) {+ TEST_CompactRange(level, begin, end);+ }+}++void DBImpl::TEST_CompactRange(int level, const Slice* begin,const Slice* end) {+ assert(level >= 0);+ assert(level + 1 < config::kNumLevels);++ InternalKey begin_storage, end_storage;++ ManualCompaction manual;+ manual.level = level;+ manual.done = false;+ if (begin == NULL) {+ manual.begin = NULL;+ } else {+ begin_storage = InternalKey(*begin, kMaxSequenceNumber, kValueTypeForSeek);+ manual.begin = &begin_storage;+ }+ if (end == NULL) {+ manual.end = NULL;+ } else {+ end_storage = InternalKey(*end, 0, static_cast<ValueType>(0));+ manual.end = &end_storage;+ }++ MutexLock l(&mutex_);+ while (!manual.done) {+ while (manual_compaction_ != NULL) {+ bg_cv_.Wait();+ }+ manual_compaction_ = &manual;+ MaybeScheduleCompaction();+ while (manual_compaction_ == &manual) {+ bg_cv_.Wait();+ }+ }+}++Status DBImpl::TEST_CompactMemTable() {+ MutexLock l(&mutex_);+ LoggerId self;+ AcquireLoggingResponsibility(&self);+ Status s = MakeRoomForWrite(true /* force compaction */);+ ReleaseLoggingResponsibility(&self);+ if (s.ok()) {+ // Wait until the compaction completes+ while (imm_ != NULL && bg_error_.ok()) {+ bg_cv_.Wait();+ }+ if (imm_ != NULL) {+ s = bg_error_;+ }+ }+ return s;+}++void DBImpl::MaybeScheduleCompaction() {+ mutex_.AssertHeld();+ if (bg_compaction_scheduled_) {+ // Already scheduled+ } else if (shutting_down_.Acquire_Load()) {+ // DB is being deleted; no more background compactions+ } else if (imm_ == NULL &&+ manual_compaction_ == NULL &&+ !versions_->NeedsCompaction()) {+ // No work to be done+ } else {+ bg_compaction_scheduled_ = true;+ env_->Schedule(&DBImpl::BGWork, this);+ }+}++void DBImpl::BGWork(void* db) {+ reinterpret_cast<DBImpl*>(db)->BackgroundCall();+}++void DBImpl::BackgroundCall() {+ MutexLock l(&mutex_);+ assert(bg_compaction_scheduled_);+ if (!shutting_down_.Acquire_Load()) {+ BackgroundCompaction();+ }+ bg_compaction_scheduled_ = false;++ // Previous compaction may have produced too many files in a level,+ // so reschedule another compaction if needed.+ MaybeScheduleCompaction();+ bg_cv_.SignalAll();+}++void DBImpl::BackgroundCompaction() {+ mutex_.AssertHeld();++ if (imm_ != NULL) {+ CompactMemTable();+ return;+ }++ Compaction* c;+ bool is_manual = (manual_compaction_ != NULL);+ InternalKey manual_end;+ if (is_manual) {+ ManualCompaction* m = manual_compaction_;+ c = versions_->CompactRange(m->level, m->begin, m->end);+ m->done = (c == NULL);+ if (c != NULL) {+ manual_end = c->input(0, c->num_input_files(0) - 1)->largest;+ }+ Log(options_.info_log,+ "Manual compaction at level-%d from %s .. %s; will stop at %s\n",+ m->level,+ (m->begin ? m->begin->DebugString().c_str() : "(begin)"),+ (m->end ? m->end->DebugString().c_str() : "(end)"),+ (m->done ? "(end)" : manual_end.DebugString().c_str()));+ } else {+ c = versions_->PickCompaction();+ }++ Status status;+ if (c == NULL) {+ // Nothing to do+ } else if (!is_manual && c->IsTrivialMove()) {+ // Move file to next level+ assert(c->num_input_files(0) == 1);+ FileMetaData* f = c->input(0, 0);+ c->edit()->DeleteFile(c->level(), f->number);+ c->edit()->AddFile(c->level() + 1, f->number, f->file_size,+ f->smallest, f->largest);+ status = versions_->LogAndApply(c->edit(), &mutex_);+ VersionSet::LevelSummaryStorage tmp;+ Log(options_.info_log, "Moved #%lld to level-%d %lld bytes %s: %s\n",+ static_cast<unsigned long long>(f->number),+ c->level() + 1,+ static_cast<unsigned long long>(f->file_size),+ status.ToString().c_str(),+ versions_->LevelSummary(&tmp));+ } else {+ CompactionState* compact = new CompactionState(c);+ status = DoCompactionWork(compact);+ CleanupCompaction(compact);+ c->ReleaseInputs();+ DeleteObsoleteFiles();+ }+ delete c;++ if (status.ok()) {+ // Done+ } else if (shutting_down_.Acquire_Load()) {+ // Ignore compaction errors found during shutting down+ } else {+ Log(options_.info_log,+ "Compaction error: %s", status.ToString().c_str());+ if (options_.paranoid_checks && bg_error_.ok()) {+ bg_error_ = status;+ }+ }++ if (is_manual) {+ ManualCompaction* m = manual_compaction_;+ if (!status.ok()) {+ m->done = true;+ }+ if (!m->done) {+ // We only compacted part of the requested range. Update *m+ // to the range that is left to be compacted.+ m->tmp_storage = manual_end;+ m->begin = &m->tmp_storage;+ }+ manual_compaction_ = NULL;+ }+}++void DBImpl::CleanupCompaction(CompactionState* compact) {+ mutex_.AssertHeld();+ if (compact->builder != NULL) {+ // May happen if we get a shutdown call in the middle of compaction+ compact->builder->Abandon();+ delete compact->builder;+ } else {+ assert(compact->outfile == NULL);+ }+ delete compact->outfile;+ for (size_t i = 0; i < compact->outputs.size(); i++) {+ const CompactionState::Output& out = compact->outputs[i];+ pending_outputs_.erase(out.number);+ }+ delete compact;+}++Status DBImpl::OpenCompactionOutputFile(CompactionState* compact) {+ assert(compact != NULL);+ assert(compact->builder == NULL);+ uint64_t file_number;+ {+ mutex_.Lock();+ file_number = versions_->NewFileNumber();+ pending_outputs_.insert(file_number);+ CompactionState::Output out;+ out.number = file_number;+ out.smallest.Clear();+ out.largest.Clear();+ compact->outputs.push_back(out);+ mutex_.Unlock();+ }++ // Make the output file+ std::string fname = TableFileName(dbname_, file_number);+ Status s = env_->NewWritableFile(fname, &compact->outfile);+ if (s.ok()) {+ compact->builder = new TableBuilder(options_, compact->outfile);+ }+ return s;+}++Status DBImpl::FinishCompactionOutputFile(CompactionState* compact,+ Iterator* input) {+ assert(compact != NULL);+ assert(compact->outfile != NULL);+ assert(compact->builder != NULL);++ const uint64_t output_number = compact->current_output()->number;+ assert(output_number != 0);++ // Check for iterator errors+ Status s = input->status();+ const uint64_t current_entries = compact->builder->NumEntries();+ if (s.ok()) {+ s = compact->builder->Finish();+ } else {+ compact->builder->Abandon();+ }+ const uint64_t current_bytes = compact->builder->FileSize();+ compact->current_output()->file_size = current_bytes;+ compact->total_bytes += current_bytes;+ delete compact->builder;+ compact->builder = NULL;++ // Finish and check for file errors+ if (s.ok()) {+ s = compact->outfile->Sync();+ }+ if (s.ok()) {+ s = compact->outfile->Close();+ }+ delete compact->outfile;+ compact->outfile = NULL;++ if (s.ok() && current_entries > 0) {+ // Verify that the table is usable+ Iterator* iter = table_cache_->NewIterator(ReadOptions(),+ output_number,+ current_bytes);+ s = iter->status();+ delete iter;+ if (s.ok()) {+ Log(options_.info_log,+ "Generated table #%llu: %lld keys, %lld bytes",+ (unsigned long long) output_number,+ (unsigned long long) current_entries,+ (unsigned long long) current_bytes);+ }+ }+ return s;+}+++Status DBImpl::InstallCompactionResults(CompactionState* compact) {+ mutex_.AssertHeld();+ Log(options_.info_log, "Compacted %d@%d + %d@%d files => %lld bytes",+ compact->compaction->num_input_files(0),+ compact->compaction->level(),+ compact->compaction->num_input_files(1),+ compact->compaction->level() + 1,+ static_cast<long long>(compact->total_bytes));++ // Add compaction outputs+ compact->compaction->AddInputDeletions(compact->compaction->edit());+ const int level = compact->compaction->level();+ for (size_t i = 0; i < compact->outputs.size(); i++) {+ const CompactionState::Output& out = compact->outputs[i];+ compact->compaction->edit()->AddFile(+ level + 1,+ out.number, out.file_size, out.smallest, out.largest);+ }+ return versions_->LogAndApply(compact->compaction->edit(), &mutex_);+}++Status DBImpl::DoCompactionWork(CompactionState* compact) {+ const uint64_t start_micros = env_->NowMicros();+ int64_t imm_micros = 0; // Micros spent doing imm_ compactions++ Log(options_.info_log, "Compacting %d@%d + %d@%d files",+ compact->compaction->num_input_files(0),+ compact->compaction->level(),+ compact->compaction->num_input_files(1),+ compact->compaction->level() + 1);++ assert(versions_->NumLevelFiles(compact->compaction->level()) > 0);+ assert(compact->builder == NULL);+ assert(compact->outfile == NULL);+ if (snapshots_.empty()) {+ compact->smallest_snapshot = versions_->LastSequence();+ } else {+ compact->smallest_snapshot = snapshots_.oldest()->number_;+ }++ // Release mutex while we're actually doing the compaction work+ mutex_.Unlock();++ Iterator* input = versions_->MakeInputIterator(compact->compaction);+ input->SeekToFirst();+ Status status;+ ParsedInternalKey ikey;+ std::string current_user_key;+ bool has_current_user_key = false;+ SequenceNumber last_sequence_for_key = kMaxSequenceNumber;+ for (; input->Valid() && !shutting_down_.Acquire_Load(); ) {+ // Prioritize immutable compaction work+ if (has_imm_.NoBarrier_Load() != NULL) {+ const uint64_t imm_start = env_->NowMicros();+ mutex_.Lock();+ if (imm_ != NULL) {+ CompactMemTable();+ bg_cv_.SignalAll(); // Wakeup MakeRoomForWrite() if necessary+ }+ mutex_.Unlock();+ imm_micros += (env_->NowMicros() - imm_start);+ }++ Slice key = input->key();+ if (compact->compaction->ShouldStopBefore(key) &&+ compact->builder != NULL) {+ status = FinishCompactionOutputFile(compact, input);+ if (!status.ok()) {+ break;+ }+ }++ // Handle key/value, add to state, etc.+ bool drop = false;+ if (!ParseInternalKey(key, &ikey)) {+ // Do not hide error keys+ current_user_key.clear();+ has_current_user_key = false;+ last_sequence_for_key = kMaxSequenceNumber;+ } else {+ if (!has_current_user_key ||+ user_comparator()->Compare(ikey.user_key,+ Slice(current_user_key)) != 0) {+ // First occurrence of this user key+ current_user_key.assign(ikey.user_key.data(), ikey.user_key.size());+ has_current_user_key = true;+ last_sequence_for_key = kMaxSequenceNumber;+ }++ if (last_sequence_for_key <= compact->smallest_snapshot) {+ // Hidden by an newer entry for same user key+ drop = true; // (A)+ } else if (ikey.type == kTypeDeletion &&+ ikey.sequence <= compact->smallest_snapshot &&+ compact->compaction->IsBaseLevelForKey(ikey.user_key)) {+ // For this user key:+ // (1) there is no data in higher levels+ // (2) data in lower levels will have larger sequence numbers+ // (3) data in layers that are being compacted here and have+ // smaller sequence numbers will be dropped in the next+ // few iterations of this loop (by rule (A) above).+ // Therefore this deletion marker is obsolete and can be dropped.+ drop = true;+ }++ last_sequence_for_key = ikey.sequence;+ }+#if 0+ Log(options_.info_log,+ " Compact: %s, seq %d, type: %d %d, drop: %d, is_base: %d, "+ "%d smallest_snapshot: %d",+ ikey.user_key.ToString().c_str(),+ (int)ikey.sequence, ikey.type, kTypeValue, drop,+ compact->compaction->IsBaseLevelForKey(ikey.user_key),+ (int)last_sequence_for_key, (int)compact->smallest_snapshot);+#endif++ if (!drop) {+ // Open output file if necessary+ if (compact->builder == NULL) {+ status = OpenCompactionOutputFile(compact);+ if (!status.ok()) {+ break;+ }+ }+ if (compact->builder->NumEntries() == 0) {+ compact->current_output()->smallest.DecodeFrom(key);+ }+ compact->current_output()->largest.DecodeFrom(key);+ compact->builder->Add(key, input->value());++ // Close output file if it is big enough+ if (compact->builder->FileSize() >=+ compact->compaction->MaxOutputFileSize()) {+ status = FinishCompactionOutputFile(compact, input);+ if (!status.ok()) {+ break;+ }+ }+ }++ input->Next();+ }++ if (status.ok() && shutting_down_.Acquire_Load()) {+ status = Status::IOError("Deleting DB during compaction");+ }+ if (status.ok() && compact->builder != NULL) {+ status = FinishCompactionOutputFile(compact, input);+ }+ if (status.ok()) {+ status = input->status();+ }+ delete input;+ input = NULL;++ CompactionStats stats;+ stats.micros = env_->NowMicros() - start_micros - imm_micros;+ for (int which = 0; which < 2; which++) {+ for (int i = 0; i < compact->compaction->num_input_files(which); i++) {+ stats.bytes_read += compact->compaction->input(which, i)->file_size;+ }+ }+ for (size_t i = 0; i < compact->outputs.size(); i++) {+ stats.bytes_written += compact->outputs[i].file_size;+ }++ mutex_.Lock();+ stats_[compact->compaction->level() + 1].Add(stats);++ if (status.ok()) {+ status = InstallCompactionResults(compact);+ }+ VersionSet::LevelSummaryStorage tmp;+ Log(options_.info_log,+ "compacted to: %s", versions_->LevelSummary(&tmp));+ return status;+}++namespace {+struct IterState {+ port::Mutex* mu;+ Version* version;+ MemTable* mem;+ MemTable* imm;+};++static void CleanupIteratorState(void* arg1, void* arg2) {+ IterState* state = reinterpret_cast<IterState*>(arg1);+ state->mu->Lock();+ state->mem->Unref();+ if (state->imm != NULL) state->imm->Unref();+ state->version->Unref();+ state->mu->Unlock();+ delete state;+}+} // namespace++Iterator* DBImpl::NewInternalIterator(const ReadOptions& options,+ SequenceNumber* latest_snapshot) {+ IterState* cleanup = new IterState;+ mutex_.Lock();+ *latest_snapshot = versions_->LastSequence();++ // Collect together all needed child iterators+ std::vector<Iterator*> list;+ list.push_back(mem_->NewIterator());+ mem_->Ref();+ if (imm_ != NULL) {+ list.push_back(imm_->NewIterator());+ imm_->Ref();+ }+ versions_->current()->AddIterators(options, &list);+ Iterator* internal_iter =+ NewMergingIterator(&internal_comparator_, &list[0], list.size());+ versions_->current()->Ref();++ cleanup->mu = &mutex_;+ cleanup->mem = mem_;+ cleanup->imm = imm_;+ cleanup->version = versions_->current();+ internal_iter->RegisterCleanup(CleanupIteratorState, cleanup, NULL);++ mutex_.Unlock();+ return internal_iter;+}++Iterator* DBImpl::TEST_NewInternalIterator() {+ SequenceNumber ignored;+ return NewInternalIterator(ReadOptions(), &ignored);+}++int64_t DBImpl::TEST_MaxNextLevelOverlappingBytes() {+ MutexLock l(&mutex_);+ return versions_->MaxNextLevelOverlappingBytes();+}++Status DBImpl::Get(const ReadOptions& options,+ const Slice& key,+ std::string* value) {+ Status s;+ MutexLock l(&mutex_);+ SequenceNumber snapshot;+ if (options.snapshot != NULL) {+ snapshot = reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_;+ } else {+ snapshot = versions_->LastSequence();+ }++ MemTable* mem = mem_;+ MemTable* imm = imm_;+ Version* current = versions_->current();+ mem->Ref();+ if (imm != NULL) imm->Ref();+ current->Ref();++ bool have_stat_update = false;+ Version::GetStats stats;++ // Unlock while reading from files and memtables+ {+ mutex_.Unlock();+ // First look in the memtable, then in the immutable memtable (if any).+ LookupKey lkey(key, snapshot);+ if (mem->Get(lkey, value, &s)) {+ // Done+ } else if (imm != NULL && imm->Get(lkey, value, &s)) {+ // Done+ } else {+ s = current->Get(options, lkey, value, &stats);+ have_stat_update = true;+ }+ mutex_.Lock();+ }++ if (have_stat_update && current->UpdateStats(stats)) {+ MaybeScheduleCompaction();+ }+ mem->Unref();+ if (imm != NULL) imm->Unref();+ current->Unref();+ return s;+}++Iterator* DBImpl::NewIterator(const ReadOptions& options) {+ SequenceNumber latest_snapshot;+ Iterator* internal_iter = NewInternalIterator(options, &latest_snapshot);+ return NewDBIterator(+ &dbname_, env_, user_comparator(), internal_iter,+ (options.snapshot != NULL+ ? reinterpret_cast<const SnapshotImpl*>(options.snapshot)->number_+ : latest_snapshot));+}++const Snapshot* DBImpl::GetSnapshot() {+ MutexLock l(&mutex_);+ return snapshots_.New(versions_->LastSequence());+}++void DBImpl::ReleaseSnapshot(const Snapshot* s) {+ MutexLock l(&mutex_);+ snapshots_.Delete(reinterpret_cast<const SnapshotImpl*>(s));+}++// Convenience methods+Status DBImpl::Put(const WriteOptions& o, const Slice& key, const Slice& val) {+ return DB::Put(o, key, val);+}++Status DBImpl::Delete(const WriteOptions& options, const Slice& key) {+ return DB::Delete(options, key);+}++// There is at most one thread that is the current logger. This call+// waits until preceding logger(s) have finished and becomes the+// current logger.+void DBImpl::AcquireLoggingResponsibility(LoggerId* self) {+ while (logger_ != NULL) {+ logger_cv_.Wait();+ }+ logger_ = self;+}++void DBImpl::ReleaseLoggingResponsibility(LoggerId* self) {+ assert(logger_ == self);+ logger_ = NULL;+ logger_cv_.SignalAll();+}++Status DBImpl::Write(const WriteOptions& options, WriteBatch* updates) {+ Status status;+ MutexLock l(&mutex_);+ LoggerId self;+ AcquireLoggingResponsibility(&self);+ status = MakeRoomForWrite(false); // May temporarily release lock and wait+ uint64_t last_sequence = versions_->LastSequence();+ if (status.ok()) {+ WriteBatchInternal::SetSequence(updates, last_sequence + 1);+ last_sequence += WriteBatchInternal::Count(updates);++ // Add to log and apply to memtable. We can release the lock during+ // this phase since the "logger_" flag protects against concurrent+ // loggers and concurrent writes into mem_.+ {+ assert(logger_ == &self);+ mutex_.Unlock();+ status = log_->AddRecord(WriteBatchInternal::Contents(updates));+ if (status.ok() && options.sync) {+ status = logfile_->Sync();+ }+ if (status.ok()) {+ status = WriteBatchInternal::InsertInto(updates, mem_);+ }+ mutex_.Lock();+ assert(logger_ == &self);+ }++ versions_->SetLastSequence(last_sequence);+ }+ ReleaseLoggingResponsibility(&self);+ return status;+}++// REQUIRES: mutex_ is held+// REQUIRES: this thread is the current logger+Status DBImpl::MakeRoomForWrite(bool force) {+ mutex_.AssertHeld();+ assert(logger_ != NULL);+ bool allow_delay = !force;+ Status s;+ while (true) {+ if (!bg_error_.ok()) {+ // Yield previous error+ s = bg_error_;+ break;+ } else if (+ allow_delay &&+ versions_->NumLevelFiles(0) >= config::kL0_SlowdownWritesTrigger) {+ // We are getting close to hitting a hard limit on the number of+ // L0 files. Rather than delaying a single write by several+ // seconds when we hit the hard limit, start delaying each+ // individual write by 1ms to reduce latency variance. Also,+ // this delay hands over some CPU to the compaction thread in+ // case it is sharing the same core as the writer.+ mutex_.Unlock();+ env_->SleepForMicroseconds(1000);+ allow_delay = false; // Do not delay a single write more than once+ mutex_.Lock();+ } else if (!force &&+ (mem_->ApproximateMemoryUsage() <= options_.write_buffer_size)) {+ // There is room in current memtable+ break;+ } else if (imm_ != NULL) {+ // We have filled up the current memtable, but the previous+ // one is still being compacted, so we wait.+ bg_cv_.Wait();+ } else if (versions_->NumLevelFiles(0) >= config::kL0_StopWritesTrigger) {+ // There are too many level-0 files.+ Log(options_.info_log, "waiting...\n");+ bg_cv_.Wait();+ } else {+ // Attempt to switch to a new memtable and trigger compaction of old+ assert(versions_->PrevLogNumber() == 0);+ uint64_t new_log_number = versions_->NewFileNumber();+ WritableFile* lfile = NULL;+ s = env_->NewWritableFile(LogFileName(dbname_, new_log_number), &lfile);+ if (!s.ok()) {+ break;+ }+ delete log_;+ delete logfile_;+ logfile_ = lfile;+ logfile_number_ = new_log_number;+ log_ = new log::Writer(lfile);+ imm_ = mem_;+ has_imm_.Release_Store(imm_);+ mem_ = new MemTable(internal_comparator_);+ mem_->Ref();+ force = false; // Do not force another compaction if have room+ MaybeScheduleCompaction();+ }+ }+ return s;+}++bool DBImpl::GetProperty(const Slice& property, std::string* value) {+ value->clear();++ MutexLock l(&mutex_);+ Slice in = property;+ Slice prefix("leveldb.");+ if (!in.starts_with(prefix)) return false;+ in.remove_prefix(prefix.size());++ if (in.starts_with("num-files-at-level")) {+ in.remove_prefix(strlen("num-files-at-level"));+ uint64_t level;+ bool ok = ConsumeDecimalNumber(&in, &level) && in.empty();+ if (!ok || level >= config::kNumLevels) {+ return false;+ } else {+ char buf[100];+ snprintf(buf, sizeof(buf), "%d",+ versions_->NumLevelFiles(static_cast<int>(level)));+ *value = buf;+ return true;+ }+ } else if (in == "stats") {+ char buf[200];+ snprintf(buf, sizeof(buf),+ " Compactions\n"+ "Level Files Size(MB) Time(sec) Read(MB) Write(MB)\n"+ "--------------------------------------------------\n"+ );+ value->append(buf);+ for (int level = 0; level < config::kNumLevels; level++) {+ int files = versions_->NumLevelFiles(level);+ if (stats_[level].micros > 0 || files > 0) {+ snprintf(+ buf, sizeof(buf),+ "%3d %8d %8.0f %9.0f %8.0f %9.0f\n",+ level,+ files,+ versions_->NumLevelBytes(level) / 1048576.0,+ stats_[level].micros / 1e6,+ stats_[level].bytes_read / 1048576.0,+ stats_[level].bytes_written / 1048576.0);+ value->append(buf);+ }+ }+ return true;+ } else if (in == "sstables") {+ *value = versions_->current()->DebugString();+ return true;+ }++ return false;+}++void DBImpl::GetApproximateSizes(+ const Range* range, int n,+ uint64_t* sizes) {+ // TODO(opt): better implementation+ Version* v;+ {+ MutexLock l(&mutex_);+ versions_->current()->Ref();+ v = versions_->current();+ }++ for (int i = 0; i < n; i++) {+ // Convert user_key into a corresponding internal key.+ InternalKey k1(range[i].start, kMaxSequenceNumber, kValueTypeForSeek);+ InternalKey k2(range[i].limit, kMaxSequenceNumber, kValueTypeForSeek);+ uint64_t start = versions_->ApproximateOffsetOf(v, k1);+ uint64_t limit = versions_->ApproximateOffsetOf(v, k2);+ sizes[i] = (limit >= start ? limit - start : 0);+ }++ {+ MutexLock l(&mutex_);+ v->Unref();+ }+}++// Default implementations of convenience methods that subclasses of DB+// can call if they wish+Status DB::Put(const WriteOptions& opt, const Slice& key, const Slice& value) {+ WriteBatch batch;+ batch.Put(key, value);+ return Write(opt, &batch);+}++Status DB::Delete(const WriteOptions& opt, const Slice& key) {+ WriteBatch batch;+ batch.Delete(key);+ return Write(opt, &batch);+}++DB::~DB() { }++Status DB::Open(const Options& options, const std::string& dbname,+ DB** dbptr) {+ *dbptr = NULL;++ DBImpl* impl = new DBImpl(options, dbname);+ impl->mutex_.Lock();+ VersionEdit edit;+ Status s = impl->Recover(&edit); // Handles create_if_missing, error_if_exists+ if (s.ok()) {+ uint64_t new_log_number = impl->versions_->NewFileNumber();+ WritableFile* lfile;+ s = options.env->NewWritableFile(LogFileName(dbname, new_log_number),+ &lfile);+ if (s.ok()) {+ edit.SetLogNumber(new_log_number);+ impl->logfile_ = lfile;+ impl->logfile_number_ = new_log_number;+ impl->log_ = new log::Writer(lfile);+ s = impl->versions_->LogAndApply(&edit, &impl->mutex_);+ }+ if (s.ok()) {+ impl->DeleteObsoleteFiles();+ impl->MaybeScheduleCompaction();+ }+ }+ impl->mutex_.Unlock();+ if (s.ok()) {+ *dbptr = impl;+ } else {+ delete impl;+ }+ return s;+}++Snapshot::~Snapshot() {+}++Status DestroyDB(const std::string& dbname, const Options& options) {+ Env* env = options.env;+ std::vector<std::string> filenames;+ // Ignore error in case directory does not exist+ env->GetChildren(dbname, &filenames);+ if (filenames.empty()) {+ return Status::OK();+ }++ FileLock* lock;+ const std::string lockname = LockFileName(dbname);+ Status result = env->LockFile(lockname, &lock);+ if (result.ok()) {+ uint64_t number;+ FileType type;+ for (size_t i = 0; i < filenames.size(); i++) {+ if (ParseFileName(filenames[i], &number, &type) &&+ filenames[i] != lockname) { // Lock file will be deleted at end+ Status del = env->DeleteFile(dbname + "/" + filenames[i]);+ if (result.ok() && !del.ok()) {+ result = del;+ }+ }+ }+ env->UnlockFile(lock); // Ignore error since state is already gone+ env->DeleteFile(lockname);+ env->DeleteDir(dbname); // Ignore error in case dir contains other files+ }+ return result;+}++} // namespace leveldb
+ cbits/leveldb/db/db_impl.h view
@@ -0,0 +1,192 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_DB_IMPL_H_+#define STORAGE_LEVELDB_DB_DB_IMPL_H_++#include <set>+#include "db/dbformat.h"+#include "db/log_writer.h"+#include "db/snapshot.h"+#include "leveldb/db.h"+#include "leveldb/env.h"+#include "port/port.h"++namespace leveldb {++class MemTable;+class TableCache;+class Version;+class VersionEdit;+class VersionSet;++class DBImpl : public DB {+ public:+ DBImpl(const Options& options, const std::string& dbname);+ virtual ~DBImpl();++ // Implementations of the DB interface+ virtual Status Put(const WriteOptions&, const Slice& key, const Slice& value);+ virtual Status Delete(const WriteOptions&, const Slice& key);+ virtual Status Write(const WriteOptions& options, WriteBatch* updates);+ virtual Status Get(const ReadOptions& options,+ const Slice& key,+ std::string* value);+ virtual Iterator* NewIterator(const ReadOptions&);+ virtual const Snapshot* GetSnapshot();+ virtual void ReleaseSnapshot(const Snapshot* snapshot);+ virtual bool GetProperty(const Slice& property, std::string* value);+ virtual void GetApproximateSizes(const Range* range, int n, uint64_t* sizes);+ virtual void CompactRange(const Slice* begin, const Slice* end);++ // Extra methods (for testing) that are not in the public DB interface++ // Compact any files in the named level that overlap [*begin,*end]+ void TEST_CompactRange(int level, const Slice* begin, const Slice* end);++ // Force current memtable contents to be compacted.+ Status TEST_CompactMemTable();++ // Return an internal iterator over the current state of the database.+ // The keys of this iterator are internal keys (see format.h).+ // The returned iterator should be deleted when no longer needed.+ Iterator* TEST_NewInternalIterator();++ // Return the maximum overlapping data (in bytes) at next level for any+ // file at a level >= 1.+ int64_t TEST_MaxNextLevelOverlappingBytes();++ private:+ friend class DB;++ Iterator* NewInternalIterator(const ReadOptions&,+ SequenceNumber* latest_snapshot);++ Status NewDB();++ // Recover the descriptor from persistent storage. May do a significant+ // amount of work to recover recently logged updates. Any changes to+ // be made to the descriptor are added to *edit.+ Status Recover(VersionEdit* edit);++ void MaybeIgnoreError(Status* s) const;++ // Delete any unneeded files and stale in-memory entries.+ void DeleteObsoleteFiles();++ // Compact the in-memory write buffer to disk. Switches to a new+ // log-file/memtable and writes a new descriptor iff successful.+ Status CompactMemTable();++ Status RecoverLogFile(uint64_t log_number,+ VersionEdit* edit,+ SequenceNumber* max_sequence);++ Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base);++ // Only thread is allowed to log at a time.+ struct LoggerId { }; // Opaque identifier for logging thread+ void AcquireLoggingResponsibility(LoggerId* self);+ void ReleaseLoggingResponsibility(LoggerId* self);++ Status MakeRoomForWrite(bool force /* compact even if there is room? */);++ struct CompactionState;++ void MaybeScheduleCompaction();+ static void BGWork(void* db);+ void BackgroundCall();+ void BackgroundCompaction();+ void CleanupCompaction(CompactionState* compact);+ Status DoCompactionWork(CompactionState* compact);++ Status OpenCompactionOutputFile(CompactionState* compact);+ Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input);+ Status InstallCompactionResults(CompactionState* compact);++ // Constant after construction+ Env* const env_;+ const InternalKeyComparator internal_comparator_;+ const Options options_; // options_.comparator == &internal_comparator_+ bool owns_info_log_;+ bool owns_cache_;+ const std::string dbname_;++ // table_cache_ provides its own synchronization+ TableCache* table_cache_;++ // Lock over the persistent DB state. Non-NULL iff successfully acquired.+ FileLock* db_lock_;++ // State below is protected by mutex_+ port::Mutex mutex_;+ port::AtomicPointer shutting_down_;+ port::CondVar bg_cv_; // Signalled when background work finishes+ MemTable* mem_;+ MemTable* imm_; // Memtable being compacted+ port::AtomicPointer has_imm_; // So bg thread can detect non-NULL imm_+ WritableFile* logfile_;+ uint64_t logfile_number_;+ log::Writer* log_;+ LoggerId* logger_; // NULL, or the id of the current logging thread+ port::CondVar logger_cv_; // For threads waiting to log+ SnapshotList snapshots_;++ // Set of table files to protect from deletion because they are+ // part of ongoing compactions.+ std::set<uint64_t> pending_outputs_;++ // Has a background compaction been scheduled or is running?+ bool bg_compaction_scheduled_;++ // Information for a manual compaction+ struct ManualCompaction {+ int level;+ bool done;+ const InternalKey* begin; // NULL means beginning of key range+ const InternalKey* end; // NULL means end of key range+ InternalKey tmp_storage; // Used to keep track of compaction progress+ };+ ManualCompaction* manual_compaction_;++ VersionSet* versions_;++ // Have we encountered a background error in paranoid mode?+ Status bg_error_;++ // Per level compaction stats. stats_[level] stores the stats for+ // compactions that produced data for the specified "level".+ struct CompactionStats {+ int64_t micros;+ int64_t bytes_read;+ int64_t bytes_written;++ CompactionStats() : micros(0), bytes_read(0), bytes_written(0) { }++ void Add(const CompactionStats& c) {+ this->micros += c.micros;+ this->bytes_read += c.bytes_read;+ this->bytes_written += c.bytes_written;+ }+ };+ CompactionStats stats_[config::kNumLevels];++ // No copying allowed+ DBImpl(const DBImpl&);+ void operator=(const DBImpl&);++ const Comparator* user_comparator() const {+ return internal_comparator_.user_comparator();+ }+};++// Sanitize db options. The caller should delete result.info_log if+// it is not equal to src.info_log.+extern Options SanitizeOptions(const std::string& db,+ const InternalKeyComparator* icmp,+ const Options& src);++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_DB_IMPL_H_
+ cbits/leveldb/db/db_iter.cc view
@@ -0,0 +1,299 @@+// 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 "db/db_iter.h"++#include "db/filename.h"+#include "db/dbformat.h"+#include "leveldb/env.h"+#include "leveldb/iterator.h"+#include "port/port.h"+#include "util/logging.h"+#include "util/mutexlock.h"++namespace leveldb {++#if 0+static void DumpInternalIter(Iterator* iter) {+ for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {+ ParsedInternalKey k;+ if (!ParseInternalKey(iter->key(), &k)) {+ fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str());+ } else {+ fprintf(stderr, "@ '%s'\n", k.DebugString().c_str());+ }+ }+}+#endif++namespace {++// Memtables and sstables that make the DB representation contain+// (userkey,seq,type) => uservalue entries. DBIter+// combines multiple entries for the same userkey found in the DB+// representation into a single entry while accounting for sequence+// numbers, deletion markers, overwrites, etc.+class DBIter: public Iterator {+ public:+ // Which direction is the iterator currently moving?+ // (1) When moving forward, the internal iterator is positioned at+ // the exact entry that yields this->key(), this->value()+ // (2) When moving backwards, the internal iterator is positioned+ // just before all entries whose user key == this->key().+ enum Direction {+ kForward,+ kReverse+ };++ DBIter(const std::string* dbname, Env* env,+ const Comparator* cmp, Iterator* iter, SequenceNumber s)+ : dbname_(dbname),+ env_(env),+ user_comparator_(cmp),+ iter_(iter),+ sequence_(s),+ direction_(kForward),+ valid_(false) {+ }+ virtual ~DBIter() {+ delete iter_;+ }+ virtual bool Valid() const { return valid_; }+ virtual Slice key() const {+ assert(valid_);+ return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_;+ }+ virtual Slice value() const {+ assert(valid_);+ return (direction_ == kForward) ? iter_->value() : saved_value_;+ }+ virtual Status status() const {+ if (status_.ok()) {+ return iter_->status();+ } else {+ return status_;+ }+ }++ virtual void Next();+ virtual void Prev();+ virtual void Seek(const Slice& target);+ virtual void SeekToFirst();+ virtual void SeekToLast();++ private:+ void FindNextUserEntry(bool skipping, std::string* skip);+ void FindPrevUserEntry();+ bool ParseKey(ParsedInternalKey* key);++ inline void SaveKey(const Slice& k, std::string* dst) {+ dst->assign(k.data(), k.size());+ }++ inline void ClearSavedValue() {+ if (saved_value_.capacity() > 1048576) {+ std::string empty;+ swap(empty, saved_value_);+ } else {+ saved_value_.clear();+ }+ }++ const std::string* const dbname_;+ Env* const env_;+ const Comparator* const user_comparator_;+ Iterator* const iter_;+ SequenceNumber const sequence_;++ Status status_;+ std::string saved_key_; // == current key when direction_==kReverse+ std::string saved_value_; // == current raw value when direction_==kReverse+ Direction direction_;+ bool valid_;++ // No copying allowed+ DBIter(const DBIter&);+ void operator=(const DBIter&);+};++inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {+ if (!ParseInternalKey(iter_->key(), ikey)) {+ status_ = Status::Corruption("corrupted internal key in DBIter");+ return false;+ } else {+ return true;+ }+}++void DBIter::Next() {+ assert(valid_);++ if (direction_ == kReverse) { // Switch directions?+ direction_ = kForward;+ // iter_ is pointing just before the entries for this->key(),+ // so advance into the range of entries for this->key() and then+ // use the normal skipping code below.+ if (!iter_->Valid()) {+ iter_->SeekToFirst();+ } else {+ iter_->Next();+ }+ if (!iter_->Valid()) {+ valid_ = false;+ saved_key_.clear();+ return;+ }+ }++ // Temporarily use saved_key_ as storage for key to skip.+ std::string* skip = &saved_key_;+ SaveKey(ExtractUserKey(iter_->key()), skip);+ FindNextUserEntry(true, skip);+}++void DBIter::FindNextUserEntry(bool skipping, std::string* skip) {+ // Loop until we hit an acceptable entry to yield+ assert(iter_->Valid());+ assert(direction_ == kForward);+ do {+ ParsedInternalKey ikey;+ if (ParseKey(&ikey) && ikey.sequence <= sequence_) {+ switch (ikey.type) {+ case kTypeDeletion:+ // Arrange to skip all upcoming entries for this key since+ // they are hidden by this deletion.+ SaveKey(ikey.user_key, skip);+ skipping = true;+ break;+ case kTypeValue:+ if (skipping &&+ user_comparator_->Compare(ikey.user_key, *skip) <= 0) {+ // Entry hidden+ } else {+ valid_ = true;+ saved_key_.clear();+ return;+ }+ break;+ }+ }+ iter_->Next();+ } while (iter_->Valid());+ saved_key_.clear();+ valid_ = false;+}++void DBIter::Prev() {+ assert(valid_);++ if (direction_ == kForward) { // Switch directions?+ // iter_ is pointing at the current entry. Scan backwards until+ // the key changes so we can use the normal reverse scanning code.+ assert(iter_->Valid()); // Otherwise valid_ would have been false+ SaveKey(ExtractUserKey(iter_->key()), &saved_key_);+ while (true) {+ iter_->Prev();+ if (!iter_->Valid()) {+ valid_ = false;+ saved_key_.clear();+ ClearSavedValue();+ return;+ }+ if (user_comparator_->Compare(ExtractUserKey(iter_->key()),+ saved_key_) < 0) {+ break;+ }+ }+ direction_ = kReverse;+ }++ FindPrevUserEntry();+}++void DBIter::FindPrevUserEntry() {+ assert(direction_ == kReverse);++ ValueType value_type = kTypeDeletion;+ if (iter_->Valid()) {+ do {+ ParsedInternalKey ikey;+ if (ParseKey(&ikey) && ikey.sequence <= sequence_) {+ if ((value_type != kTypeDeletion) &&+ user_comparator_->Compare(ikey.user_key, saved_key_) < 0) {+ // We encountered a non-deleted value in entries for previous keys,+ break;+ }+ value_type = ikey.type;+ if (value_type == kTypeDeletion) {+ saved_key_.clear();+ ClearSavedValue();+ } else {+ Slice raw_value = iter_->value();+ if (saved_value_.capacity() > raw_value.size() + 1048576) {+ std::string empty;+ swap(empty, saved_value_);+ }+ SaveKey(ExtractUserKey(iter_->key()), &saved_key_);+ saved_value_.assign(raw_value.data(), raw_value.size());+ }+ }+ iter_->Prev();+ } while (iter_->Valid());+ }++ if (value_type == kTypeDeletion) {+ // End+ valid_ = false;+ saved_key_.clear();+ ClearSavedValue();+ direction_ = kForward;+ } else {+ valid_ = true;+ }+}++void DBIter::Seek(const Slice& target) {+ direction_ = kForward;+ ClearSavedValue();+ saved_key_.clear();+ AppendInternalKey(+ &saved_key_, ParsedInternalKey(target, sequence_, kValueTypeForSeek));+ iter_->Seek(saved_key_);+ if (iter_->Valid()) {+ FindNextUserEntry(false, &saved_key_ /* temporary storage */);+ } else {+ valid_ = false;+ }+}++void DBIter::SeekToFirst() {+ direction_ = kForward;+ ClearSavedValue();+ iter_->SeekToFirst();+ if (iter_->Valid()) {+ FindNextUserEntry(false, &saved_key_ /* temporary storage */);+ } else {+ valid_ = false;+ }+}++void DBIter::SeekToLast() {+ direction_ = kReverse;+ ClearSavedValue();+ iter_->SeekToLast();+ FindPrevUserEntry();+}++} // anonymous namespace++Iterator* NewDBIterator(+ const std::string* dbname,+ Env* env,+ const Comparator* user_key_comparator,+ Iterator* internal_iter,+ const SequenceNumber& sequence) {+ return new DBIter(dbname, env, user_key_comparator, internal_iter, sequence);+}++} // namespace leveldb
+ cbits/leveldb/db/db_iter.h view
@@ -0,0 +1,26 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_DB_ITER_H_+#define STORAGE_LEVELDB_DB_DB_ITER_H_++#include <stdint.h>+#include "leveldb/db.h"+#include "db/dbformat.h"++namespace leveldb {++// Return a new iterator that converts internal keys (yielded by+// "*internal_iter") that were live at the specified "sequence" number+// into appropriate user keys.+extern Iterator* NewDBIterator(+ const std::string* dbname,+ Env* env,+ const Comparator* user_key_comparator,+ Iterator* internal_iter,+ const SequenceNumber& sequence);++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_DB_ITER_H_
+ cbits/leveldb/db/db_test.cc view
@@ -0,0 +1,1711 @@+// 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/db.h"+#include "db/db_impl.h"+#include "db/filename.h"+#include "db/version_set.h"+#include "db/write_batch_internal.h"+#include "leveldb/env.h"+#include "leveldb/table.h"+#include "util/logging.h"+#include "util/mutexlock.h"+#include "util/testharness.h"+#include "util/testutil.h"++namespace leveldb {++static std::string RandomString(Random* rnd, int len) {+ std::string r;+ test::RandomString(rnd, len, &r);+ return r;+}++// Special Env used to delay background operations+class SpecialEnv : public EnvWrapper {+ public:+ // sstable Sync() calls are blocked while this pointer is non-NULL.+ port::AtomicPointer delay_sstable_sync_;++ // Simulate no-space errors while this pointer is non-NULL.+ port::AtomicPointer no_space_;++ explicit SpecialEnv(Env* base) : EnvWrapper(base) {+ delay_sstable_sync_.Release_Store(NULL);+ no_space_.Release_Store(NULL);+ }++ Status NewWritableFile(const std::string& f, WritableFile** r) {+ class SSTableFile : public WritableFile {+ private:+ SpecialEnv* env_;+ WritableFile* base_;++ public:+ SSTableFile(SpecialEnv* env, WritableFile* base)+ : env_(env),+ base_(base) {+ }+ ~SSTableFile() { delete base_; }+ Status Append(const Slice& data) {+ if (env_->no_space_.Acquire_Load() != NULL) {+ // Drop writes on the floor+ return Status::OK();+ } else {+ return base_->Append(data);+ }+ }+ Status Close() { return base_->Close(); }+ Status Flush() { return base_->Flush(); }+ Status Sync() {+ while (env_->delay_sstable_sync_.Acquire_Load() != NULL) {+ env_->SleepForMicroseconds(100000);+ }+ return base_->Sync();+ }+ };++ Status s = target()->NewWritableFile(f, r);+ if (s.ok()) {+ if (strstr(f.c_str(), ".sst") != NULL) {+ *r = new SSTableFile(this, *r);+ }+ }+ return s;+ }+};++class DBTest {+ public:+ std::string dbname_;+ SpecialEnv* env_;+ DB* db_;++ Options last_options_;++ DBTest() : env_(new SpecialEnv(Env::Default())) {+ dbname_ = test::TmpDir() + "/db_test";+ DestroyDB(dbname_, Options());+ db_ = NULL;+ Reopen();+ }++ ~DBTest() {+ delete db_;+ DestroyDB(dbname_, Options());+ delete env_;+ }++ DBImpl* dbfull() {+ return reinterpret_cast<DBImpl*>(db_);+ }++ void Reopen(Options* options = NULL) {+ ASSERT_OK(TryReopen(options));+ }++ void DestroyAndReopen(Options* options = NULL) {+ delete db_;+ db_ = NULL;+ DestroyDB(dbname_, Options());+ ASSERT_OK(TryReopen(options));+ }++ Status TryReopen(Options* options) {+ delete db_;+ db_ = NULL;+ Options opts;+ if (options != NULL) {+ opts = *options;+ } else {+ opts.create_if_missing = true;+ }+ last_options_ = opts;++ return DB::Open(opts, dbname_, &db_);+ }++ Status Put(const std::string& k, const std::string& v) {+ return db_->Put(WriteOptions(), k, v);+ }++ Status Delete(const std::string& k) {+ return db_->Delete(WriteOptions(), k);+ }++ std::string Get(const std::string& k, const Snapshot* snapshot = NULL) {+ ReadOptions options;+ options.snapshot = snapshot;+ std::string result;+ Status s = db_->Get(options, k, &result);+ if (s.IsNotFound()) {+ result = "NOT_FOUND";+ } else if (!s.ok()) {+ result = s.ToString();+ }+ return result;+ }++ // Return a string that contains all key,value pairs in order,+ // formatted like "(k1->v1)(k2->v2)".+ std::string Contents() {+ std::vector<std::string> forward;+ std::string result;+ Iterator* iter = db_->NewIterator(ReadOptions());+ for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {+ std::string s = IterStatus(iter);+ result.push_back('(');+ result.append(s);+ result.push_back(')');+ forward.push_back(s);+ }++ // Check reverse iteration results are the reverse of forward results+ int matched = 0;+ for (iter->SeekToLast(); iter->Valid(); iter->Prev()) {+ ASSERT_LT(matched, forward.size());+ ASSERT_EQ(IterStatus(iter), forward[forward.size() - matched - 1]);+ matched++;+ }+ ASSERT_EQ(matched, forward.size());++ delete iter;+ return result;+ }++ std::string AllEntriesFor(const Slice& user_key) {+ Iterator* iter = dbfull()->TEST_NewInternalIterator();+ InternalKey target(user_key, kMaxSequenceNumber, kTypeValue);+ iter->Seek(target.Encode());+ std::string result;+ if (!iter->status().ok()) {+ result = iter->status().ToString();+ } else {+ result = "[ ";+ bool first = true;+ while (iter->Valid()) {+ ParsedInternalKey ikey;+ if (!ParseInternalKey(iter->key(), &ikey)) {+ result += "CORRUPTED";+ } else {+ if (last_options_.comparator->Compare(+ ikey.user_key, user_key) != 0) {+ break;+ }+ if (!first) {+ result += ", ";+ }+ first = false;+ switch (ikey.type) {+ case kTypeValue:+ result += iter->value().ToString();+ break;+ case kTypeDeletion:+ result += "DEL";+ break;+ }+ }+ iter->Next();+ }+ if (!first) {+ result += " ";+ }+ result += "]";+ }+ delete iter;+ return result;+ }++ int NumTableFilesAtLevel(int level) {+ std::string property;+ ASSERT_TRUE(+ db_->GetProperty("leveldb.num-files-at-level" + NumberToString(level),+ &property));+ return atoi(property.c_str());+ }++ int TotalTableFiles() {+ int result = 0;+ for (int level = 0; level < config::kNumLevels; level++) {+ result += NumTableFilesAtLevel(level);+ }+ return result;+ }++ // Return spread of files per level+ std::string FilesPerLevel() {+ std::string result;+ int last_non_zero_offset = 0;+ for (int level = 0; level < config::kNumLevels; level++) {+ int f = NumTableFilesAtLevel(level);+ char buf[100];+ snprintf(buf, sizeof(buf), "%s%d", (level ? "," : ""), f);+ result += buf;+ if (f > 0) {+ last_non_zero_offset = result.size();+ }+ }+ result.resize(last_non_zero_offset);+ return result;+ }++ int CountFiles() {+ std::vector<std::string> files;+ env_->GetChildren(dbname_, &files);+ return static_cast<int>(files.size());+ }++ uint64_t Size(const Slice& start, const Slice& limit) {+ Range r(start, limit);+ uint64_t size;+ db_->GetApproximateSizes(&r, 1, &size);+ return size;+ }++ void Compact(const Slice& start, const Slice& limit) {+ db_->CompactRange(&start, &limit);+ }++ // Do n memtable compactions, each of which produces an sstable+ // covering the range [small,large].+ void MakeTables(int n, const std::string& small, const std::string& large) {+ for (int i = 0; i < n; i++) {+ Put(small, "begin");+ Put(large, "end");+ dbfull()->TEST_CompactMemTable();+ }+ }++ // Prevent pushing of new sstables into deeper levels by adding+ // tables that cover a specified range to all levels.+ void FillLevels(const std::string& smallest, const std::string& largest) {+ MakeTables(config::kNumLevels, smallest, largest);+ }++ void DumpFileCounts(const char* label) {+ fprintf(stderr, "---\n%s:\n", label);+ fprintf(stderr, "maxoverlap: %lld\n",+ static_cast<long long>(+ dbfull()->TEST_MaxNextLevelOverlappingBytes()));+ for (int level = 0; level < config::kNumLevels; level++) {+ int num = NumTableFilesAtLevel(level);+ if (num > 0) {+ fprintf(stderr, " level %3d : %d files\n", level, num);+ }+ }+ }++ std::string DumpSSTableList() {+ std::string property;+ db_->GetProperty("leveldb.sstables", &property);+ return property;+ }++ std::string IterStatus(Iterator* iter) {+ std::string result;+ if (iter->Valid()) {+ result = iter->key().ToString() + "->" + iter->value().ToString();+ } else {+ result = "(invalid)";+ }+ return result;+ }+};++TEST(DBTest, Empty) {+ ASSERT_TRUE(db_ != NULL);+ ASSERT_EQ("NOT_FOUND", Get("foo"));+}++TEST(DBTest, ReadWrite) {+ ASSERT_OK(Put("foo", "v1"));+ ASSERT_EQ("v1", Get("foo"));+ ASSERT_OK(Put("bar", "v2"));+ ASSERT_OK(Put("foo", "v3"));+ ASSERT_EQ("v3", Get("foo"));+ ASSERT_EQ("v2", Get("bar"));+}++TEST(DBTest, PutDeleteGet) {+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v1"));+ ASSERT_EQ("v1", Get("foo"));+ ASSERT_OK(db_->Put(WriteOptions(), "foo", "v2"));+ ASSERT_EQ("v2", Get("foo"));+ ASSERT_OK(db_->Delete(WriteOptions(), "foo"));+ ASSERT_EQ("NOT_FOUND", Get("foo"));+}++TEST(DBTest, GetFromImmutableLayer) {+ Options options;+ options.env = env_;+ options.write_buffer_size = 100000; // Small write buffer+ Reopen(&options);++ ASSERT_OK(Put("foo", "v1"));+ ASSERT_EQ("v1", Get("foo"));++ env_->delay_sstable_sync_.Release_Store(env_); // Block sync calls+ Put("k1", std::string(100000, 'x')); // Fill memtable+ Put("k2", std::string(100000, 'y')); // Trigger compaction+ ASSERT_EQ("v1", Get("foo"));+ env_->delay_sstable_sync_.Release_Store(NULL); // Release sync calls+}++TEST(DBTest, GetFromVersions) {+ ASSERT_OK(Put("foo", "v1"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ("v1", Get("foo"));+}++TEST(DBTest, GetSnapshot) {+ // Try with both a short key and a long key+ for (int i = 0; i < 2; i++) {+ std::string key = (i == 0) ? std::string("foo") : std::string(200, 'x');+ ASSERT_OK(Put(key, "v1"));+ const Snapshot* s1 = db_->GetSnapshot();+ ASSERT_OK(Put(key, "v2"));+ ASSERT_EQ("v2", Get(key));+ ASSERT_EQ("v1", Get(key, s1));+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ("v2", Get(key));+ ASSERT_EQ("v1", Get(key, s1));+ db_->ReleaseSnapshot(s1);+ }+}++TEST(DBTest, GetLevel0Ordering) {+ // Check that we process level-0 files in correct order. The code+ // below generates two level-0 files where the earlier one comes+ // before the later one in the level-0 file list since the earlier+ // one has a smaller "smallest" key.+ ASSERT_OK(Put("bar", "b"));+ ASSERT_OK(Put("foo", "v1"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_OK(Put("foo", "v2"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ("v2", Get("foo"));+}++TEST(DBTest, GetOrderedByLevels) {+ ASSERT_OK(Put("foo", "v1"));+ Compact("a", "z");+ ASSERT_EQ("v1", Get("foo"));+ ASSERT_OK(Put("foo", "v2"));+ ASSERT_EQ("v2", Get("foo"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ("v2", Get("foo"));+}++TEST(DBTest, GetPicksCorrectFile) {+ // Arrange to have multiple files in a non-level-0 level.+ ASSERT_OK(Put("a", "va"));+ Compact("a", "b");+ ASSERT_OK(Put("x", "vx"));+ Compact("x", "y");+ ASSERT_OK(Put("f", "vf"));+ Compact("f", "g");+ ASSERT_EQ("va", Get("a"));+ ASSERT_EQ("vf", Get("f"));+ ASSERT_EQ("vx", Get("x"));+}++TEST(DBTest, GetEncountersEmptyLevel) {+ // Arrange for the following to happen:+ // * sstable A in level 0+ // * nothing in level 1+ // * sstable B in level 2+ // Then do enough Get() calls to arrange for an automatic compaction+ // of sstable A. A bug would cause the compaction to be marked as+ // occuring at level 1 (instead of the correct level 0).++ // Step 1: First place sstables in levels 0 and 2+ int compaction_count = 0;+ while (NumTableFilesAtLevel(0) == 0 ||+ NumTableFilesAtLevel(2) == 0) {+ ASSERT_LE(compaction_count, 100) << "could not fill levels 0 and 2";+ compaction_count++;+ Put("a", "begin");+ Put("z", "end");+ dbfull()->TEST_CompactMemTable();+ }++ // Step 2: clear level 1 if necessary.+ dbfull()->TEST_CompactRange(1, NULL, NULL);+ ASSERT_EQ(NumTableFilesAtLevel(0), 1);+ ASSERT_EQ(NumTableFilesAtLevel(1), 0);+ ASSERT_EQ(NumTableFilesAtLevel(2), 1);++ // Step 3: read until level 0 compaction disappears.+ int read_count = 0;+ while (NumTableFilesAtLevel(0) > 0) {+ ASSERT_LE(read_count, 10000) << "did not trigger level 0 compaction";+ read_count++;+ ASSERT_EQ("NOT_FOUND", Get("missing"));+ }+}++TEST(DBTest, IterEmpty) {+ Iterator* iter = db_->NewIterator(ReadOptions());++ iter->SeekToFirst();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->SeekToLast();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->Seek("foo");+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ delete iter;+}++TEST(DBTest, IterSingle) {+ ASSERT_OK(Put("a", "va"));+ Iterator* iter = db_->NewIterator(ReadOptions());++ iter->SeekToFirst();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "(invalid)");+ iter->SeekToFirst();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->SeekToLast();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "(invalid)");+ iter->SeekToLast();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->Seek("");+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->Seek("a");+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->Seek("b");+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ delete iter;+}++TEST(DBTest, IterMulti) {+ ASSERT_OK(Put("a", "va"));+ ASSERT_OK(Put("b", "vb"));+ ASSERT_OK(Put("c", "vc"));+ Iterator* iter = db_->NewIterator(ReadOptions());++ iter->SeekToFirst();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "b->vb");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "c->vc");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "(invalid)");+ iter->SeekToFirst();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->SeekToLast();+ ASSERT_EQ(IterStatus(iter), "c->vc");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "b->vb");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "(invalid)");+ iter->SeekToLast();+ ASSERT_EQ(IterStatus(iter), "c->vc");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->Seek("");+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Seek("a");+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Seek("ax");+ ASSERT_EQ(IterStatus(iter), "b->vb");+ iter->Seek("b");+ ASSERT_EQ(IterStatus(iter), "b->vb");+ iter->Seek("z");+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ // Switch from reverse to forward+ iter->SeekToLast();+ iter->Prev();+ iter->Prev();+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "b->vb");++ // Switch from forward to reverse+ iter->SeekToFirst();+ iter->Next();+ iter->Next();+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "b->vb");++ // Make sure iter stays at snapshot+ ASSERT_OK(Put("a", "va2"));+ ASSERT_OK(Put("a2", "va3"));+ ASSERT_OK(Put("b", "vb2"));+ ASSERT_OK(Put("c", "vc2"));+ ASSERT_OK(Delete("b"));+ iter->SeekToFirst();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "b->vb");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "c->vc");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "(invalid)");+ iter->SeekToLast();+ ASSERT_EQ(IterStatus(iter), "c->vc");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "b->vb");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ delete iter;+}++TEST(DBTest, IterSmallAndLargeMix) {+ ASSERT_OK(Put("a", "va"));+ ASSERT_OK(Put("b", std::string(100000, 'b')));+ ASSERT_OK(Put("c", "vc"));+ ASSERT_OK(Put("d", std::string(100000, 'd')));+ ASSERT_OK(Put("e", std::string(100000, 'e')));++ Iterator* iter = db_->NewIterator(ReadOptions());++ iter->SeekToFirst();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "c->vc");+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));+ iter->Next();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ iter->SeekToLast();+ ASSERT_EQ(IterStatus(iter), "e->" + std::string(100000, 'e'));+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "d->" + std::string(100000, 'd'));+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "c->vc");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "b->" + std::string(100000, 'b'));+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "a->va");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "(invalid)");++ delete iter;+}++TEST(DBTest, IterMultiWithDelete) {+ ASSERT_OK(Put("a", "va"));+ ASSERT_OK(Put("b", "vb"));+ ASSERT_OK(Put("c", "vc"));+ ASSERT_OK(Delete("b"));+ ASSERT_EQ("NOT_FOUND", Get("b"));++ Iterator* iter = db_->NewIterator(ReadOptions());+ iter->Seek("c");+ ASSERT_EQ(IterStatus(iter), "c->vc");+ iter->Prev();+ ASSERT_EQ(IterStatus(iter), "a->va");+ delete iter;+}++TEST(DBTest, Recover) {+ ASSERT_OK(Put("foo", "v1"));+ ASSERT_OK(Put("baz", "v5"));++ Reopen();+ ASSERT_EQ("v1", Get("foo"));++ ASSERT_EQ("v1", Get("foo"));+ ASSERT_EQ("v5", Get("baz"));+ ASSERT_OK(Put("bar", "v2"));+ ASSERT_OK(Put("foo", "v3"));++ Reopen();+ ASSERT_EQ("v3", Get("foo"));+ ASSERT_OK(Put("foo", "v4"));+ ASSERT_EQ("v4", Get("foo"));+ ASSERT_EQ("v2", Get("bar"));+ ASSERT_EQ("v5", Get("baz"));+}++TEST(DBTest, RecoveryWithEmptyLog) {+ ASSERT_OK(Put("foo", "v1"));+ ASSERT_OK(Put("foo", "v2"));+ Reopen();+ Reopen();+ ASSERT_OK(Put("foo", "v3"));+ Reopen();+ ASSERT_EQ("v3", Get("foo"));+}++// Check that writes done during a memtable compaction are recovered+// if the database is shutdown during the memtable compaction.+TEST(DBTest, RecoverDuringMemtableCompaction) {+ Options options;+ options.env = env_;+ options.write_buffer_size = 1000000;+ Reopen(&options);++ // Trigger a long memtable compaction and reopen the database during it+ ASSERT_OK(Put("foo", "v1")); // Goes to 1st log file+ ASSERT_OK(Put("big1", std::string(10000000, 'x'))); // Fills memtable+ ASSERT_OK(Put("big2", std::string(1000, 'y'))); // Triggers compaction+ ASSERT_OK(Put("bar", "v2")); // Goes to new log file++ Reopen(&options);+ ASSERT_EQ("v1", Get("foo"));+ ASSERT_EQ("v2", Get("bar"));+ ASSERT_EQ(std::string(10000000, 'x'), Get("big1"));+ ASSERT_EQ(std::string(1000, 'y'), Get("big2"));+}++static std::string Key(int i) {+ char buf[100];+ snprintf(buf, sizeof(buf), "key%06d", i);+ return std::string(buf);+}++TEST(DBTest, MinorCompactionsHappen) {+ Options options;+ options.write_buffer_size = 10000;+ Reopen(&options);++ const int N = 500;++ int starting_num_tables = TotalTableFiles();+ for (int i = 0; i < N; i++) {+ ASSERT_OK(Put(Key(i), Key(i) + std::string(1000, 'v')));+ }+ int ending_num_tables = TotalTableFiles();+ ASSERT_GT(ending_num_tables, starting_num_tables);++ for (int i = 0; i < N; i++) {+ ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i)));+ }++ Reopen();++ for (int i = 0; i < N; i++) {+ ASSERT_EQ(Key(i) + std::string(1000, 'v'), Get(Key(i)));+ }+}++TEST(DBTest, RecoverWithLargeLog) {+ {+ Options options;+ Reopen(&options);+ ASSERT_OK(Put("big1", std::string(200000, '1')));+ ASSERT_OK(Put("big2", std::string(200000, '2')));+ ASSERT_OK(Put("small3", std::string(10, '3')));+ ASSERT_OK(Put("small4", std::string(10, '4')));+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);+ }++ // Make sure that if we re-open with a small write buffer size that+ // we flush table files in the middle of a large log file.+ Options options;+ options.write_buffer_size = 100000;+ Reopen(&options);+ ASSERT_EQ(NumTableFilesAtLevel(0), 3);+ ASSERT_EQ(std::string(200000, '1'), Get("big1"));+ ASSERT_EQ(std::string(200000, '2'), Get("big2"));+ ASSERT_EQ(std::string(10, '3'), Get("small3"));+ ASSERT_EQ(std::string(10, '4'), Get("small4"));+ ASSERT_GT(NumTableFilesAtLevel(0), 1);+}++TEST(DBTest, CompactionsGenerateMultipleFiles) {+ Options options;+ options.write_buffer_size = 100000000; // Large write buffer+ Reopen(&options);++ Random rnd(301);++ // Write 8MB (80 values, each 100K)+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);+ std::vector<std::string> values;+ for (int i = 0; i < 80; i++) {+ values.push_back(RandomString(&rnd, 100000));+ ASSERT_OK(Put(Key(i), values[i]));+ }++ // Reopening moves updates to level-0+ Reopen(&options);+ dbfull()->TEST_CompactRange(0, NULL, NULL);++ ASSERT_EQ(NumTableFilesAtLevel(0), 0);+ ASSERT_GT(NumTableFilesAtLevel(1), 1);+ for (int i = 0; i < 80; i++) {+ ASSERT_EQ(Get(Key(i)), values[i]);+ }+}++TEST(DBTest, RepeatedWritesToSameKey) {+ Options options;+ options.env = env_;+ options.write_buffer_size = 100000; // Small write buffer+ Reopen(&options);++ // We must have at most one file per level except for level-0,+ // which may have up to kL0_StopWritesTrigger files.+ const int kMaxFiles = config::kNumLevels + config::kL0_StopWritesTrigger;++ Random rnd(301);+ std::string value = RandomString(&rnd, 2 * options.write_buffer_size);+ for (int i = 0; i < 5 * kMaxFiles; i++) {+ Put("key", value);+ ASSERT_LE(TotalTableFiles(), kMaxFiles);+ fprintf(stderr, "after %d: %d files\n", int(i+1), TotalTableFiles());+ }+}++TEST(DBTest, SparseMerge) {+ Options options;+ options.compression = kNoCompression;+ Reopen(&options);++ FillLevels("A", "Z");++ // Suppose there is:+ // small amount of data with prefix A+ // large amount of data with prefix B+ // small amount of data with prefix C+ // and that recent updates have made small changes to all three prefixes.+ // Check that we do not do a compaction that merges all of B in one shot.+ const std::string value(1000, 'x');+ Put("A", "va");+ // Write approximately 100MB of "B" values+ for (int i = 0; i < 100000; i++) {+ char key[100];+ snprintf(key, sizeof(key), "B%010d", i);+ Put(key, value);+ }+ Put("C", "vc");+ dbfull()->TEST_CompactMemTable();+ dbfull()->TEST_CompactRange(0, NULL, NULL);++ // Make sparse update+ Put("A", "va2");+ Put("B100", "bvalue2");+ Put("C", "vc2");+ dbfull()->TEST_CompactMemTable();++ // Compactions should not cause us to create a situation where+ // a file overlaps too much data at the next level.+ ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);+ dbfull()->TEST_CompactRange(0, NULL, NULL);+ ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);+ dbfull()->TEST_CompactRange(1, NULL, NULL);+ ASSERT_LE(dbfull()->TEST_MaxNextLevelOverlappingBytes(), 20*1048576);+}++static bool Between(uint64_t val, uint64_t low, uint64_t high) {+ bool result = (val >= low) && (val <= high);+ if (!result) {+ fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",+ (unsigned long long)(val),+ (unsigned long long)(low),+ (unsigned long long)(high));+ }+ return result;+}++TEST(DBTest, ApproximateSizes) {+ Options options;+ options.write_buffer_size = 100000000; // Large write buffer+ options.compression = kNoCompression;+ DestroyAndReopen();++ ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));+ Reopen(&options);+ ASSERT_TRUE(Between(Size("", "xyz"), 0, 0));++ // Write 8MB (80 values, each 100K)+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);+ const int N = 80;+ Random rnd(301);+ for (int i = 0; i < N; i++) {+ ASSERT_OK(Put(Key(i), RandomString(&rnd, 100000)));+ }++ // 0 because GetApproximateSizes() does not account for memtable space+ ASSERT_TRUE(Between(Size("", Key(50)), 0, 0));++ // Check sizes across recovery by reopening a few times+ for (int run = 0; run < 3; run++) {+ Reopen(&options);++ for (int compact_start = 0; compact_start < N; compact_start += 10) {+ for (int i = 0; i < N; i += 10) {+ ASSERT_TRUE(Between(Size("", Key(i)), 100000*i, 100000*i + 10000));+ ASSERT_TRUE(Between(Size("", Key(i)+".suffix"),+ 100000 * (i+1), 100000 * (i+1) + 10000));+ ASSERT_TRUE(Between(Size(Key(i), Key(i+10)),+ 100000 * 10, 100000 * 10 + 10000));+ }+ ASSERT_TRUE(Between(Size("", Key(50)), 5000000, 5010000));+ ASSERT_TRUE(Between(Size("", Key(50)+".suffix"), 5100000, 5110000));++ std::string cstart_str = Key(compact_start);+ std::string cend_str = Key(compact_start + 9);+ Slice cstart = cstart_str;+ Slice cend = cend_str;+ dbfull()->TEST_CompactRange(0, &cstart, &cend);+ }++ ASSERT_EQ(NumTableFilesAtLevel(0), 0);+ ASSERT_GT(NumTableFilesAtLevel(1), 0);+ }+}++TEST(DBTest, ApproximateSizes_MixOfSmallAndLarge) {+ Options options;+ options.compression = kNoCompression;+ Reopen();++ Random rnd(301);+ std::string big1 = RandomString(&rnd, 100000);+ ASSERT_OK(Put(Key(0), RandomString(&rnd, 10000)));+ ASSERT_OK(Put(Key(1), RandomString(&rnd, 10000)));+ ASSERT_OK(Put(Key(2), big1));+ ASSERT_OK(Put(Key(3), RandomString(&rnd, 10000)));+ ASSERT_OK(Put(Key(4), big1));+ ASSERT_OK(Put(Key(5), RandomString(&rnd, 10000)));+ ASSERT_OK(Put(Key(6), RandomString(&rnd, 300000)));+ ASSERT_OK(Put(Key(7), RandomString(&rnd, 10000)));++ // Check sizes across recovery by reopening a few times+ for (int run = 0; run < 3; run++) {+ Reopen(&options);++ ASSERT_TRUE(Between(Size("", Key(0)), 0, 0));+ ASSERT_TRUE(Between(Size("", Key(1)), 10000, 11000));+ ASSERT_TRUE(Between(Size("", Key(2)), 20000, 21000));+ ASSERT_TRUE(Between(Size("", Key(3)), 120000, 121000));+ ASSERT_TRUE(Between(Size("", Key(4)), 130000, 131000));+ ASSERT_TRUE(Between(Size("", Key(5)), 230000, 231000));+ ASSERT_TRUE(Between(Size("", Key(6)), 240000, 241000));+ ASSERT_TRUE(Between(Size("", Key(7)), 540000, 541000));+ ASSERT_TRUE(Between(Size("", Key(8)), 550000, 551000));++ ASSERT_TRUE(Between(Size(Key(3), Key(5)), 110000, 111000));++ dbfull()->TEST_CompactRange(0, NULL, NULL);+ }+}++TEST(DBTest, IteratorPinsRef) {+ Put("foo", "hello");++ // Get iterator that will yield the current contents of the DB.+ Iterator* iter = db_->NewIterator(ReadOptions());++ // Write to force compactions+ Put("foo", "newvalue1");+ for (int i = 0; i < 100; i++) {+ ASSERT_OK(Put(Key(i), Key(i) + std::string(100000, 'v'))); // 100K values+ }+ Put("foo", "newvalue2");++ iter->SeekToFirst();+ ASSERT_TRUE(iter->Valid());+ ASSERT_EQ("foo", iter->key().ToString());+ ASSERT_EQ("hello", iter->value().ToString());+ iter->Next();+ ASSERT_TRUE(!iter->Valid());+ delete iter;+}++TEST(DBTest, Snapshot) {+ Put("foo", "v1");+ const Snapshot* s1 = db_->GetSnapshot();+ Put("foo", "v2");+ const Snapshot* s2 = db_->GetSnapshot();+ Put("foo", "v3");+ const Snapshot* s3 = db_->GetSnapshot();++ Put("foo", "v4");+ ASSERT_EQ("v1", Get("foo", s1));+ ASSERT_EQ("v2", Get("foo", s2));+ ASSERT_EQ("v3", Get("foo", s3));+ ASSERT_EQ("v4", Get("foo"));++ db_->ReleaseSnapshot(s3);+ ASSERT_EQ("v1", Get("foo", s1));+ ASSERT_EQ("v2", Get("foo", s2));+ ASSERT_EQ("v4", Get("foo"));++ db_->ReleaseSnapshot(s1);+ ASSERT_EQ("v2", Get("foo", s2));+ ASSERT_EQ("v4", Get("foo"));++ db_->ReleaseSnapshot(s2);+ ASSERT_EQ("v4", Get("foo"));+}++TEST(DBTest, HiddenValuesAreRemoved) {+ Random rnd(301);+ FillLevels("a", "z");++ std::string big = RandomString(&rnd, 50000);+ Put("foo", big);+ Put("pastfoo", "v");+ const Snapshot* snapshot = db_->GetSnapshot();+ Put("foo", "tiny");+ Put("pastfoo2", "v2"); // Advance sequence number one more++ ASSERT_OK(dbfull()->TEST_CompactMemTable());+ ASSERT_GT(NumTableFilesAtLevel(0), 0);++ ASSERT_EQ(big, Get("foo", snapshot));+ ASSERT_TRUE(Between(Size("", "pastfoo"), 50000, 60000));+ db_->ReleaseSnapshot(snapshot);+ ASSERT_EQ(AllEntriesFor("foo"), "[ tiny, " + big + " ]");+ Slice x("x");+ dbfull()->TEST_CompactRange(0, NULL, &x);+ ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");+ ASSERT_EQ(NumTableFilesAtLevel(0), 0);+ ASSERT_GE(NumTableFilesAtLevel(1), 1);+ dbfull()->TEST_CompactRange(1, NULL, &x);+ ASSERT_EQ(AllEntriesFor("foo"), "[ tiny ]");++ ASSERT_TRUE(Between(Size("", "pastfoo"), 0, 1000));+}++TEST(DBTest, DeletionMarkers1) {+ Put("foo", "v1");+ ASSERT_OK(dbfull()->TEST_CompactMemTable());+ const int last = config::kMaxMemCompactLevel;+ ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level++ // Place a table at level last-1 to prevent merging with preceding mutation+ Put("a", "begin");+ Put("z", "end");+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ(NumTableFilesAtLevel(last), 1);+ ASSERT_EQ(NumTableFilesAtLevel(last-1), 1);++ Delete("foo");+ Put("foo", "v2");+ ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]");+ ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2+ ASSERT_EQ(AllEntriesFor("foo"), "[ v2, DEL, v1 ]");+ Slice z("z");+ dbfull()->TEST_CompactRange(last-2, NULL, &z);+ // DEL eliminated, but v1 remains because we aren't compacting that level+ // (DEL can be eliminated because v2 hides v1).+ ASSERT_EQ(AllEntriesFor("foo"), "[ v2, v1 ]");+ dbfull()->TEST_CompactRange(last-1, NULL, NULL);+ // Merging last-1 w/ last, so we are the base level for "foo", so+ // DEL is removed. (as is v1).+ ASSERT_EQ(AllEntriesFor("foo"), "[ v2 ]");+}++TEST(DBTest, DeletionMarkers2) {+ Put("foo", "v1");+ ASSERT_OK(dbfull()->TEST_CompactMemTable());+ const int last = config::kMaxMemCompactLevel;+ ASSERT_EQ(NumTableFilesAtLevel(last), 1); // foo => v1 is now in last level++ // Place a table at level last-1 to prevent merging with preceding mutation+ Put("a", "begin");+ Put("z", "end");+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ(NumTableFilesAtLevel(last), 1);+ ASSERT_EQ(NumTableFilesAtLevel(last-1), 1);++ Delete("foo");+ ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");+ ASSERT_OK(dbfull()->TEST_CompactMemTable()); // Moves to level last-2+ ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");+ dbfull()->TEST_CompactRange(last-2, NULL, NULL);+ // DEL kept: "last" file overlaps+ ASSERT_EQ(AllEntriesFor("foo"), "[ DEL, v1 ]");+ dbfull()->TEST_CompactRange(last-1, NULL, NULL);+ // Merging last-1 w/ last, so we are the base level for "foo", so+ // DEL is removed. (as is v1).+ ASSERT_EQ(AllEntriesFor("foo"), "[ ]");+}++TEST(DBTest, OverlapInLevel0) {+ ASSERT_EQ(config::kMaxMemCompactLevel, 2) << "Fix test to match config";++ // Fill levels 1 and 2 to disable the pushing of new memtables to levels > 0.+ ASSERT_OK(Put("100", "v100"));+ ASSERT_OK(Put("999", "v999"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_OK(Delete("100"));+ ASSERT_OK(Delete("999"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ("0,1,1", FilesPerLevel());++ // Make files spanning the following ranges in level-0:+ // files[0] 200 .. 900+ // files[1] 300 .. 500+ // Note that files are sorted by smallest key.+ ASSERT_OK(Put("300", "v300"));+ ASSERT_OK(Put("500", "v500"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_OK(Put("200", "v200"));+ ASSERT_OK(Put("600", "v600"));+ ASSERT_OK(Put("900", "v900"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ("2,1,1", FilesPerLevel());++ // Compact away the placeholder files we created initially+ dbfull()->TEST_CompactRange(1, NULL, NULL);+ dbfull()->TEST_CompactRange(2, NULL, NULL);+ ASSERT_EQ("2", FilesPerLevel());++ // Do a memtable compaction. Before bug-fix, the compaction would+ // not detect the overlap with level-0 files and would incorrectly place+ // the deletion in a deeper level.+ ASSERT_OK(Delete("600"));+ dbfull()->TEST_CompactMemTable();+ ASSERT_EQ("3", FilesPerLevel());+ ASSERT_EQ("NOT_FOUND", Get("600"));+}++TEST(DBTest, L0_CompactionBug_Issue44_a) {+ Reopen();+ ASSERT_OK(Put("b", "v"));+ Reopen();+ ASSERT_OK(Delete("b"));+ ASSERT_OK(Delete("a"));+ Reopen();+ ASSERT_OK(Delete("a"));+ Reopen();+ ASSERT_OK(Put("a", "v"));+ Reopen();+ Reopen();+ ASSERT_EQ("(a->v)", Contents());+ env_->SleepForMicroseconds(1000000); // Wait for compaction to finish+ ASSERT_EQ("(a->v)", Contents());+}++TEST(DBTest, L0_CompactionBug_Issue44_b) {+ Reopen();+ Put("","");+ Reopen();+ Delete("e");+ Put("","");+ Reopen();+ Put("c", "cv");+ Reopen();+ Put("","");+ Reopen();+ Put("","");+ env_->SleepForMicroseconds(1000000); // Wait for compaction to finish+ Reopen();+ Put("d","dv");+ Reopen();+ Put("","");+ Reopen();+ Delete("d");+ Delete("b");+ Reopen();+ ASSERT_EQ("(->)(c->cv)", Contents());+ env_->SleepForMicroseconds(1000000); // Wait for compaction to finish+ ASSERT_EQ("(->)(c->cv)", Contents());+}++TEST(DBTest, ComparatorCheck) {+ class NewComparator : public Comparator {+ public:+ virtual const char* Name() const { return "leveldb.NewComparator"; }+ virtual int Compare(const Slice& a, const Slice& b) const {+ return BytewiseComparator()->Compare(a, b);+ }+ virtual void FindShortestSeparator(std::string* s, const Slice& l) const {+ BytewiseComparator()->FindShortestSeparator(s, l);+ }+ virtual void FindShortSuccessor(std::string* key) const {+ BytewiseComparator()->FindShortSuccessor(key);+ }+ };+ NewComparator cmp;+ Options new_options;+ new_options.comparator = &cmp;+ Status s = TryReopen(&new_options);+ ASSERT_TRUE(!s.ok());+ ASSERT_TRUE(s.ToString().find("comparator") != std::string::npos)+ << s.ToString();+}++TEST(DBTest, CustomComparator) {+ class NumberComparator : public Comparator {+ public:+ virtual const char* Name() const { return "test.NumberComparator"; }+ virtual int Compare(const Slice& a, const Slice& b) const {+ return ToNumber(a) - ToNumber(b);+ }+ virtual void FindShortestSeparator(std::string* s, const Slice& l) const {+ ToNumber(*s); // Check format+ ToNumber(l); // Check format+ }+ virtual void FindShortSuccessor(std::string* key) const {+ ToNumber(*key); // Check format+ }+ private:+ static int ToNumber(const Slice& x) {+ // Check that there are no extra characters.+ ASSERT_TRUE(x.size() >= 2 && x[0] == '[' && x[x.size()-1] == ']')+ << EscapeString(x);+ int val;+ char ignored;+ ASSERT_TRUE(sscanf(x.ToString().c_str(), "[%i]%c", &val, &ignored) == 1)+ << EscapeString(x);+ return val;+ }+ };+ NumberComparator cmp;+ Options new_options;+ new_options.create_if_missing = true;+ new_options.comparator = &cmp;+ new_options.write_buffer_size = 1000; // Compact more often+ DestroyAndReopen(&new_options);+ ASSERT_OK(Put("[10]", "ten"));+ ASSERT_OK(Put("[0x14]", "twenty"));+ for (int i = 0; i < 2; i++) {+ ASSERT_EQ("ten", Get("[10]"));+ ASSERT_EQ("ten", Get("[0xa]"));+ ASSERT_EQ("twenty", Get("[20]"));+ ASSERT_EQ("twenty", Get("[0x14]"));+ Compact("[0]", "[9999]");+ }++ for (int run = 0; run < 2; run++) {+ for (int i = 0; i < 1000; i++) {+ char buf[100];+ snprintf(buf, sizeof(buf), "[%d]", i*10);+ ASSERT_OK(Put(buf, buf));+ }+ Compact("[0]", "[1000000]");+ }+}++TEST(DBTest, ManualCompaction) {+ ASSERT_EQ(config::kMaxMemCompactLevel, 2)+ << "Need to update this test to match kMaxMemCompactLevel";++ MakeTables(3, "p", "q");+ ASSERT_EQ("1,1,1", FilesPerLevel());++ // Compaction range falls before files+ Compact("", "c");+ ASSERT_EQ("1,1,1", FilesPerLevel());++ // Compaction range falls after files+ Compact("r", "z");+ ASSERT_EQ("1,1,1", FilesPerLevel());++ // Compaction range overlaps files+ Compact("p1", "p9");+ ASSERT_EQ("0,0,1", FilesPerLevel());++ // Populate a different range+ MakeTables(3, "c", "e");+ ASSERT_EQ("1,1,2", FilesPerLevel());++ // Compact just the new range+ Compact("b", "f");+ ASSERT_EQ("0,0,2", FilesPerLevel());++ // Compact all+ MakeTables(1, "a", "z");+ ASSERT_EQ("0,1,2", FilesPerLevel());+ db_->CompactRange(NULL, NULL);+ ASSERT_EQ("0,0,1", FilesPerLevel());+}++TEST(DBTest, DBOpen_Options) {+ std::string dbname = test::TmpDir() + "/db_options_test";+ DestroyDB(dbname, Options());++ // Does not exist, and create_if_missing == false: error+ DB* db = NULL;+ Options opts;+ opts.create_if_missing = false;+ Status s = DB::Open(opts, dbname, &db);+ ASSERT_TRUE(strstr(s.ToString().c_str(), "does not exist") != NULL);+ ASSERT_TRUE(db == NULL);++ // Does not exist, and create_if_missing == true: OK+ opts.create_if_missing = true;+ s = DB::Open(opts, dbname, &db);+ ASSERT_OK(s);+ ASSERT_TRUE(db != NULL);++ delete db;+ db = NULL;++ // Does exist, and error_if_exists == true: error+ opts.create_if_missing = false;+ opts.error_if_exists = true;+ s = DB::Open(opts, dbname, &db);+ ASSERT_TRUE(strstr(s.ToString().c_str(), "exists") != NULL);+ ASSERT_TRUE(db == NULL);++ // Does exist, and error_if_exists == false: OK+ opts.create_if_missing = true;+ opts.error_if_exists = false;+ s = DB::Open(opts, dbname, &db);+ ASSERT_OK(s);+ ASSERT_TRUE(db != NULL);++ delete db;+ db = NULL;+}++// Check that number of files does not grow when we are out of space+TEST(DBTest, NoSpace) {+ Options options;+ options.env = env_;+ Reopen(&options);++ ASSERT_OK(Put("foo", "v1"));+ ASSERT_EQ("v1", Get("foo"));+ Compact("a", "z");+ const int num_files = CountFiles();+ env_->no_space_.Release_Store(env_); // Force out-of-space errors+ for (int i = 0; i < 10; i++) {+ for (int level = 0; level < config::kNumLevels-1; level++) {+ dbfull()->TEST_CompactRange(level, NULL, NULL);+ }+ }+ env_->no_space_.Release_Store(NULL);+ ASSERT_LT(CountFiles(), num_files + 5);+}++TEST(DBTest, FilesDeletedAfterCompaction) {+ ASSERT_OK(Put("foo", "v2"));+ Compact("a", "z");+ const int num_files = CountFiles();+ for (int i = 0; i < 10; i++) {+ ASSERT_OK(Put("foo", "v2"));+ Compact("a", "z");+ }+ ASSERT_EQ(CountFiles(), num_files);+}++// Multi-threaded test:+namespace {++static const int kNumThreads = 4;+static const int kTestSeconds = 10;+static const int kNumKeys = 1000;++struct MTState {+ DBTest* test;+ port::AtomicPointer stop;+ port::AtomicPointer counter[kNumThreads];+ port::AtomicPointer thread_done[kNumThreads];+};++struct MTThread {+ MTState* state;+ int id;+};++static void MTThreadBody(void* arg) {+ MTThread* t = reinterpret_cast<MTThread*>(arg);+ int id = t->id;+ DB* db = t->state->test->db_;+ uintptr_t counter = 0;+ fprintf(stderr, "... starting thread %d\n", id);+ Random rnd(1000 + id);+ std::string value;+ char valbuf[1500];+ while (t->state->stop.Acquire_Load() == NULL) {+ t->state->counter[id].Release_Store(reinterpret_cast<void*>(counter));++ int key = rnd.Uniform(kNumKeys);+ char keybuf[20];+ snprintf(keybuf, sizeof(keybuf), "%016d", key);++ if (rnd.OneIn(2)) {+ // Write values of the form <key, my id, counter>.+ // We add some padding for force compactions.+ snprintf(valbuf, sizeof(valbuf), "%d.%d.%-1000d",+ key, id, static_cast<int>(counter));+ ASSERT_OK(db->Put(WriteOptions(), Slice(keybuf), Slice(valbuf)));+ } else {+ // Read a value and verify that it matches the pattern written above.+ Status s = db->Get(ReadOptions(), Slice(keybuf), &value);+ if (s.IsNotFound()) {+ // Key has not yet been written+ } else {+ // Check that the writer thread counter is >= the counter in the value+ ASSERT_OK(s);+ int k, w, c;+ ASSERT_EQ(3, sscanf(value.c_str(), "%d.%d.%d", &k, &w, &c)) << value;+ ASSERT_EQ(k, key);+ ASSERT_GE(w, 0);+ ASSERT_LT(w, kNumThreads);+ ASSERT_LE(c, reinterpret_cast<uintptr_t>(+ t->state->counter[w].Acquire_Load()));+ }+ }+ counter++;+ }+ t->state->thread_done[id].Release_Store(t);+ fprintf(stderr, "... stopping thread %d after %d ops\n", id, int(counter));+}++} // namespace++TEST(DBTest, MultiThreaded) {+ // Initialize state+ MTState mt;+ mt.test = this;+ mt.stop.Release_Store(0);+ for (int id = 0; id < kNumThreads; id++) {+ mt.counter[id].Release_Store(0);+ mt.thread_done[id].Release_Store(0);+ }++ // Start threads+ MTThread thread[kNumThreads];+ for (int id = 0; id < kNumThreads; id++) {+ thread[id].state = &mt;+ thread[id].id = id;+ env_->StartThread(MTThreadBody, &thread[id]);+ }++ // Let them run for a while+ env_->SleepForMicroseconds(kTestSeconds * 1000000);++ // Stop the threads and wait for them to finish+ mt.stop.Release_Store(&mt);+ for (int id = 0; id < kNumThreads; id++) {+ while (mt.thread_done[id].Acquire_Load() == NULL) {+ env_->SleepForMicroseconds(100000);+ }+ }+}++namespace {+typedef std::map<std::string, std::string> KVMap;+}++class ModelDB: public DB {+ public:+ class ModelSnapshot : public Snapshot {+ public:+ KVMap map_;+ };++ explicit ModelDB(const Options& options): options_(options) { }+ ~ModelDB() { }+ virtual Status Put(const WriteOptions& o, const Slice& k, const Slice& v) {+ return DB::Put(o, k, v);+ }+ virtual Status Delete(const WriteOptions& o, const Slice& key) {+ return DB::Delete(o, key);+ }+ virtual Status Get(const ReadOptions& options,+ const Slice& key, std::string* value) {+ assert(false); // Not implemented+ return Status::NotFound(key);+ }+ virtual Iterator* NewIterator(const ReadOptions& options) {+ if (options.snapshot == NULL) {+ KVMap* saved = new KVMap;+ *saved = map_;+ return new ModelIter(saved, true);+ } else {+ const KVMap* snapshot_state =+ &(reinterpret_cast<const ModelSnapshot*>(options.snapshot)->map_);+ return new ModelIter(snapshot_state, false);+ }+ }+ virtual const Snapshot* GetSnapshot() {+ ModelSnapshot* snapshot = new ModelSnapshot;+ snapshot->map_ = map_;+ return snapshot;+ }++ virtual void ReleaseSnapshot(const Snapshot* snapshot) {+ delete reinterpret_cast<const ModelSnapshot*>(snapshot);+ }+ virtual Status Write(const WriteOptions& options, WriteBatch* batch) {+ class Handler : public WriteBatch::Handler {+ public:+ KVMap* map_;+ virtual void Put(const Slice& key, const Slice& value) {+ (*map_)[key.ToString()] = value.ToString();+ }+ virtual void Delete(const Slice& key) {+ map_->erase(key.ToString());+ }+ };+ Handler handler;+ handler.map_ = &map_;+ return batch->Iterate(&handler);+ }++ virtual bool GetProperty(const Slice& property, std::string* value) {+ return false;+ }+ virtual void GetApproximateSizes(const Range* r, int n, uint64_t* sizes) {+ for (int i = 0; i < n; i++) {+ sizes[i] = 0;+ }+ }+ virtual void CompactRange(const Slice* start, const Slice* end) {+ }++ private:+ class ModelIter: public Iterator {+ public:+ ModelIter(const KVMap* map, bool owned)+ : map_(map), owned_(owned), iter_(map_->end()) {+ }+ ~ModelIter() {+ if (owned_) delete map_;+ }+ virtual bool Valid() const { return iter_ != map_->end(); }+ virtual void SeekToFirst() { iter_ = map_->begin(); }+ virtual void SeekToLast() {+ if (map_->empty()) {+ iter_ = map_->end();+ } else {+ iter_ = map_->find(map_->rbegin()->first);+ }+ }+ virtual void Seek(const Slice& k) {+ iter_ = map_->lower_bound(k.ToString());+ }+ virtual void Next() { ++iter_; }+ virtual void Prev() { --iter_; }+ virtual Slice key() const { return iter_->first; }+ virtual Slice value() const { return iter_->second; }+ virtual Status status() const { return Status::OK(); }+ private:+ const KVMap* const map_;+ const bool owned_; // Do we own map_+ KVMap::const_iterator iter_;+ };+ const Options options_;+ KVMap map_;+};++static std::string RandomKey(Random* rnd) {+ int len = (rnd->OneIn(3)+ ? 1 // Short sometimes to encourage collisions+ : (rnd->OneIn(100) ? rnd->Skewed(10) : rnd->Uniform(10)));+ return test::RandomKey(rnd, len);+}++static bool CompareIterators(int step,+ DB* model,+ DB* db,+ const Snapshot* model_snap,+ const Snapshot* db_snap) {+ ReadOptions options;+ options.snapshot = model_snap;+ Iterator* miter = model->NewIterator(options);+ options.snapshot = db_snap;+ Iterator* dbiter = db->NewIterator(options);+ bool ok = true;+ int count = 0;+ for (miter->SeekToFirst(), dbiter->SeekToFirst();+ ok && miter->Valid() && dbiter->Valid();+ miter->Next(), dbiter->Next()) {+ count++;+ if (miter->key().compare(dbiter->key()) != 0) {+ fprintf(stderr, "step %d: Key mismatch: '%s' vs. '%s'\n",+ step,+ EscapeString(miter->key()).c_str(),+ EscapeString(dbiter->key()).c_str());+ ok = false;+ break;+ }++ if (miter->value().compare(dbiter->value()) != 0) {+ fprintf(stderr, "step %d: Value mismatch for key '%s': '%s' vs. '%s'\n",+ step,+ EscapeString(miter->key()).c_str(),+ EscapeString(miter->value()).c_str(),+ EscapeString(miter->value()).c_str());+ ok = false;+ }+ }++ if (ok) {+ if (miter->Valid() != dbiter->Valid()) {+ fprintf(stderr, "step %d: Mismatch at end of iterators: %d vs. %d\n",+ step, miter->Valid(), dbiter->Valid());+ ok = false;+ }+ }+ fprintf(stderr, "%d entries compared: ok=%d\n", count, ok);+ delete miter;+ delete dbiter;+ return ok;+}++TEST(DBTest, Randomized) {+ Random rnd(test::RandomSeed());+ ModelDB model(last_options_);+ const int N = 10000;+ const Snapshot* model_snap = NULL;+ const Snapshot* db_snap = NULL;+ std::string k, v;+ for (int step = 0; step < N; step++) {+ if (step % 100 == 0) {+ fprintf(stderr, "Step %d of %d\n", step, N);+ }+ int p = rnd.Uniform(100);+ if (p < 45) { // Put+ k = RandomKey(&rnd);+ v = RandomString(&rnd,+ rnd.OneIn(20)+ ? 100 + rnd.Uniform(100)+ : rnd.Uniform(8));+ ASSERT_OK(model.Put(WriteOptions(), k, v));+ ASSERT_OK(db_->Put(WriteOptions(), k, v));++ } else if (p < 90) { // Delete+ k = RandomKey(&rnd);+ ASSERT_OK(model.Delete(WriteOptions(), k));+ ASSERT_OK(db_->Delete(WriteOptions(), k));+++ } else { // Multi-element batch+ WriteBatch b;+ const int num = rnd.Uniform(8);+ for (int i = 0; i < num; i++) {+ if (i == 0 || !rnd.OneIn(10)) {+ k = RandomKey(&rnd);+ } else {+ // Periodically re-use the same key from the previous iter, so+ // we have multiple entries in the write batch for the same key+ }+ if (rnd.OneIn(2)) {+ v = RandomString(&rnd, rnd.Uniform(10));+ b.Put(k, v);+ } else {+ b.Delete(k);+ }+ }+ ASSERT_OK(model.Write(WriteOptions(), &b));+ ASSERT_OK(db_->Write(WriteOptions(), &b));+ }++ if ((step % 100) == 0) {+ ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));+ ASSERT_TRUE(CompareIterators(step, &model, db_, model_snap, db_snap));+ // Save a snapshot from each DB this time that we'll use next+ // time we compare things, to make sure the current state is+ // preserved with the snapshot+ if (model_snap != NULL) model.ReleaseSnapshot(model_snap);+ if (db_snap != NULL) db_->ReleaseSnapshot(db_snap);++ Reopen();+ ASSERT_TRUE(CompareIterators(step, &model, db_, NULL, NULL));++ model_snap = model.GetSnapshot();+ db_snap = db_->GetSnapshot();+ }+ }+ if (model_snap != NULL) model.ReleaseSnapshot(model_snap);+ if (db_snap != NULL) db_->ReleaseSnapshot(db_snap);+}++std::string MakeKey(unsigned int num) {+ char buf[30];+ snprintf(buf, sizeof(buf), "%016u", num);+ return std::string(buf);+}++void BM_LogAndApply(int iters, int num_base_files) {+ std::string dbname = test::TmpDir() + "/leveldb_test_benchmark";+ DestroyDB(dbname, Options());++ DB* db = NULL;+ Options opts;+ opts.create_if_missing = true;+ Status s = DB::Open(opts, dbname, &db);+ ASSERT_OK(s);+ ASSERT_TRUE(db != NULL);++ delete db;+ db = NULL;++ Env* env = Env::Default();++ port::Mutex mu;+ MutexLock l(&mu);++ InternalKeyComparator cmp(BytewiseComparator());+ Options options;+ VersionSet vset(dbname, &options, NULL, &cmp);+ ASSERT_OK(vset.Recover());+ VersionEdit vbase;+ uint64_t fnum = 1;+ for (int i = 0; i < num_base_files; i++) {+ InternalKey start(MakeKey(2*fnum), 1, kTypeValue);+ InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion);+ vbase.AddFile(2, fnum++, 1 /* file size */, start, limit);+ }+ ASSERT_OK(vset.LogAndApply(&vbase, &mu));++ uint64_t start_micros = env->NowMicros();++ for (int i = 0; i < iters; i++) {+ VersionEdit vedit;+ vedit.DeleteFile(2, fnum);+ InternalKey start(MakeKey(2*fnum), 1, kTypeValue);+ InternalKey limit(MakeKey(2*fnum+1), 1, kTypeDeletion);+ vedit.AddFile(2, fnum++, 1 /* file size */, start, limit);+ vset.LogAndApply(&vedit, &mu);+ }+ uint64_t stop_micros = env->NowMicros();+ unsigned int us = stop_micros - start_micros;+ char buf[16];+ snprintf(buf, sizeof(buf), "%d", num_base_files);+ fprintf(stderr,+ "BM_LogAndApply/%-6s %8d iters : %9u us (%7.0f us / iter)\n",+ buf, iters, us, ((float)us) / iters);+}++} // namespace leveldb++int main(int argc, char** argv) {+ if (argc > 1 && std::string(argv[1]) == "--benchmark") {+ leveldb::BM_LogAndApply(1000, 1);+ leveldb::BM_LogAndApply(1000, 100);+ leveldb::BM_LogAndApply(1000, 10000);+ leveldb::BM_LogAndApply(100, 100000);+ return 0;+ }++ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/db/dbformat.cc view
@@ -0,0 +1,120 @@+// 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 <stdio.h>+#include "db/dbformat.h"+#include "port/port.h"+#include "util/coding.h"++namespace leveldb {++static uint64_t PackSequenceAndType(uint64_t seq, ValueType t) {+ assert(seq <= kMaxSequenceNumber);+ assert(t <= kValueTypeForSeek);+ return (seq << 8) | t;+}++void AppendInternalKey(std::string* result, const ParsedInternalKey& key) {+ result->append(key.user_key.data(), key.user_key.size());+ PutFixed64(result, PackSequenceAndType(key.sequence, key.type));+}++std::string ParsedInternalKey::DebugString() const {+ char buf[50];+ snprintf(buf, sizeof(buf), "' @ %llu : %d",+ (unsigned long long) sequence,+ int(type));+ std::string result = "'";+ result += user_key.ToString();+ result += buf;+ return result;+}++std::string InternalKey::DebugString() const {+ std::string result;+ ParsedInternalKey parsed;+ if (ParseInternalKey(rep_, &parsed)) {+ result = parsed.DebugString();+ } else {+ result = "(bad)";+ result.append(EscapeString(rep_));+ }+ return result;+}++const char* InternalKeyComparator::Name() const {+ return "leveldb.InternalKeyComparator";+}++int InternalKeyComparator::Compare(const Slice& akey, const Slice& bkey) const {+ // Order by:+ // increasing user key (according to user-supplied comparator)+ // decreasing sequence number+ // decreasing type (though sequence# should be enough to disambiguate)+ int r = user_comparator_->Compare(ExtractUserKey(akey), ExtractUserKey(bkey));+ if (r == 0) {+ const uint64_t anum = DecodeFixed64(akey.data() + akey.size() - 8);+ const uint64_t bnum = DecodeFixed64(bkey.data() + bkey.size() - 8);+ if (anum > bnum) {+ r = -1;+ } else if (anum < bnum) {+ r = +1;+ }+ }+ return r;+}++void InternalKeyComparator::FindShortestSeparator(+ std::string* start,+ const Slice& limit) const {+ // Attempt to shorten the user portion of the key+ Slice user_start = ExtractUserKey(*start);+ Slice user_limit = ExtractUserKey(limit);+ std::string tmp(user_start.data(), user_start.size());+ user_comparator_->FindShortestSeparator(&tmp, user_limit);+ if (tmp.size() < user_start.size() &&+ user_comparator_->Compare(user_start, tmp) < 0) {+ // User key has become shorter physically, but larger logically.+ // Tack on the earliest possible number to the shortened user key.+ PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek));+ assert(this->Compare(*start, tmp) < 0);+ assert(this->Compare(tmp, limit) < 0);+ start->swap(tmp);+ }+}++void InternalKeyComparator::FindShortSuccessor(std::string* key) const {+ Slice user_key = ExtractUserKey(*key);+ std::string tmp(user_key.data(), user_key.size());+ user_comparator_->FindShortSuccessor(&tmp);+ if (tmp.size() < user_key.size() &&+ user_comparator_->Compare(user_key, tmp) < 0) {+ // User key has become shorter physically, but larger logically.+ // Tack on the earliest possible number to the shortened user key.+ PutFixed64(&tmp, PackSequenceAndType(kMaxSequenceNumber,kValueTypeForSeek));+ assert(this->Compare(*key, tmp) < 0);+ key->swap(tmp);+ }+}++LookupKey::LookupKey(const Slice& user_key, SequenceNumber s) {+ size_t usize = user_key.size();+ size_t needed = usize + 13; // A conservative estimate+ char* dst;+ if (needed <= sizeof(space_)) {+ dst = space_;+ } else {+ dst = new char[needed];+ }+ start_ = dst;+ dst = EncodeVarint32(dst, usize + 8);+ kstart_ = dst;+ memcpy(dst, user_key.data(), usize);+ dst += usize;+ EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));+ dst += 8;+ end_ = dst;+}++} // namespace leveldb
+ cbits/leveldb/db/dbformat.h view
@@ -0,0 +1,215 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_FORMAT_H_+#define STORAGE_LEVELDB_DB_FORMAT_H_++#include <stdio.h>+#include "leveldb/comparator.h"+#include "leveldb/db.h"+#include "leveldb/slice.h"+#include "leveldb/table_builder.h"+#include "util/coding.h"+#include "util/logging.h"++namespace leveldb {++// Grouping of constants. We may want to make some of these+// parameters set via options.+namespace config {+static const int kNumLevels = 7;++// Level-0 compaction is started when we hit this many files.+static const int kL0_CompactionTrigger = 4;++// Soft limit on number of level-0 files. We slow down writes at this point.+static const int kL0_SlowdownWritesTrigger = 8;++// Maximum number of level-0 files. We stop writes at this point.+static const int kL0_StopWritesTrigger = 12;++// Maximum level to which a new compacted memtable is pushed if it+// does not create overlap. We try to push to level 2 to avoid the+// relatively expensive level 0=>1 compactions and to avoid some+// expensive manifest file operations. We do not push all the way to+// the largest level since that can generate a lot of wasted disk+// space if the same key space is being repeatedly overwritten.+static const int kMaxMemCompactLevel = 2;++} // namespace config++class InternalKey;++// Value types encoded as the last component of internal keys.+// DO NOT CHANGE THESE ENUM VALUES: they are embedded in the on-disk+// data structures.+enum ValueType {+ kTypeDeletion = 0x0,+ kTypeValue = 0x1+};+// kValueTypeForSeek defines the ValueType that should be passed when+// constructing a ParsedInternalKey object for seeking to a particular+// sequence number (since we sort sequence numbers in decreasing order+// and the value type is embedded as the low 8 bits in the sequence+// number in internal keys, we need to use the highest-numbered+// ValueType, not the lowest).+static const ValueType kValueTypeForSeek = kTypeValue;++typedef uint64_t SequenceNumber;++// We leave eight bits empty at the bottom so a type and sequence#+// can be packed together into 64-bits.+static const SequenceNumber kMaxSequenceNumber =+ ((0x1ull << 56) - 1);++struct ParsedInternalKey {+ Slice user_key;+ SequenceNumber sequence;+ ValueType type;++ ParsedInternalKey() { } // Intentionally left uninitialized (for speed)+ ParsedInternalKey(const Slice& u, const SequenceNumber& seq, ValueType t)+ : user_key(u), sequence(seq), type(t) { }+ std::string DebugString() const;+};++// Return the length of the encoding of "key".+inline size_t InternalKeyEncodingLength(const ParsedInternalKey& key) {+ return key.user_key.size() + 8;+}++// Append the serialization of "key" to *result.+extern void AppendInternalKey(std::string* result,+ const ParsedInternalKey& key);++// Attempt to parse an internal key from "internal_key". On success,+// stores the parsed data in "*result", and returns true.+//+// On error, returns false, leaves "*result" in an undefined state.+extern bool ParseInternalKey(const Slice& internal_key,+ ParsedInternalKey* result);++// Returns the user key portion of an internal key.+inline Slice ExtractUserKey(const Slice& internal_key) {+ assert(internal_key.size() >= 8);+ return Slice(internal_key.data(), internal_key.size() - 8);+}++inline ValueType ExtractValueType(const Slice& internal_key) {+ assert(internal_key.size() >= 8);+ const size_t n = internal_key.size();+ uint64_t num = DecodeFixed64(internal_key.data() + n - 8);+ unsigned char c = num & 0xff;+ return static_cast<ValueType>(c);+}++// A comparator for internal keys that uses a specified comparator for+// the user key portion and breaks ties by decreasing sequence number.+class InternalKeyComparator : public Comparator {+ private:+ const Comparator* user_comparator_;+ public:+ explicit InternalKeyComparator(const Comparator* c) : user_comparator_(c) { }+ virtual const char* Name() const;+ virtual int Compare(const Slice& a, const Slice& b) const;+ virtual void FindShortestSeparator(+ std::string* start,+ const Slice& limit) const;+ virtual void FindShortSuccessor(std::string* key) const;++ const Comparator* user_comparator() const { return user_comparator_; }++ int Compare(const InternalKey& a, const InternalKey& b) const;+};++// Modules in this directory should keep internal keys wrapped inside+// the following class instead of plain strings so that we do not+// incorrectly use string comparisons instead of an InternalKeyComparator.+class InternalKey {+ private:+ std::string rep_;+ public:+ InternalKey() { } // Leave rep_ as empty to indicate it is invalid+ InternalKey(const Slice& user_key, SequenceNumber s, ValueType t) {+ AppendInternalKey(&rep_, ParsedInternalKey(user_key, s, t));+ }++ void DecodeFrom(const Slice& s) { rep_.assign(s.data(), s.size()); }+ Slice Encode() const {+ assert(!rep_.empty());+ return rep_;+ }++ Slice user_key() const { return ExtractUserKey(rep_); }++ void SetFrom(const ParsedInternalKey& p) {+ rep_.clear();+ AppendInternalKey(&rep_, p);+ }++ void Clear() { rep_.clear(); }++ std::string DebugString() const;+};++inline int InternalKeyComparator::Compare(+ const InternalKey& a, const InternalKey& b) const {+ return Compare(a.Encode(), b.Encode());+}++inline bool ParseInternalKey(const Slice& internal_key,+ ParsedInternalKey* result) {+ const size_t n = internal_key.size();+ if (n < 8) return false;+ uint64_t num = DecodeFixed64(internal_key.data() + n - 8);+ unsigned char c = num & 0xff;+ result->sequence = num >> 8;+ result->type = static_cast<ValueType>(c);+ result->user_key = Slice(internal_key.data(), n - 8);+ return (c <= static_cast<unsigned char>(kTypeValue));+}++// A helper class useful for DBImpl::Get()+class LookupKey {+ public:+ // Initialize *this for looking up user_key at a snapshot with+ // the specified sequence number.+ LookupKey(const Slice& user_key, SequenceNumber sequence);++ ~LookupKey();++ // Return a key suitable for lookup in a MemTable.+ Slice memtable_key() const { return Slice(start_, end_ - start_); }++ // Return an internal key (suitable for passing to an internal iterator)+ Slice internal_key() const { return Slice(kstart_, end_ - kstart_); }++ // Return the user key+ Slice user_key() const { return Slice(kstart_, end_ - kstart_ - 8); }++ private:+ // We construct a char array of the form:+ // klength varint32 <-- start_+ // userkey char[klength] <-- kstart_+ // tag uint64+ // <-- end_+ // The array is a suitable MemTable key.+ // The suffix starting with "userkey" can be used as an InternalKey.+ const char* start_;+ const char* kstart_;+ const char* end_;+ char space_[200]; // Avoid allocation for short keys++ // No copying allowed+ LookupKey(const LookupKey&);+ void operator=(const LookupKey&);+};++inline LookupKey::~LookupKey() {+ if (start_ != space_) delete[] start_;+}++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_FORMAT_H_
+ cbits/leveldb/db/dbformat_test.cc view
@@ -0,0 +1,112 @@+// 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 "db/dbformat.h"+#include "util/logging.h"+#include "util/testharness.h"++namespace leveldb {++static std::string IKey(const std::string& user_key,+ uint64_t seq,+ ValueType vt) {+ std::string encoded;+ AppendInternalKey(&encoded, ParsedInternalKey(user_key, seq, vt));+ return encoded;+}++static std::string Shorten(const std::string& s, const std::string& l) {+ std::string result = s;+ InternalKeyComparator(BytewiseComparator()).FindShortestSeparator(&result, l);+ return result;+}++static std::string ShortSuccessor(const std::string& s) {+ std::string result = s;+ InternalKeyComparator(BytewiseComparator()).FindShortSuccessor(&result);+ return result;+}++static void TestKey(const std::string& key,+ uint64_t seq,+ ValueType vt) {+ std::string encoded = IKey(key, seq, vt);++ Slice in(encoded);+ ParsedInternalKey decoded("", 0, kTypeValue);++ ASSERT_TRUE(ParseInternalKey(in, &decoded));+ ASSERT_EQ(key, decoded.user_key.ToString());+ ASSERT_EQ(seq, decoded.sequence);+ ASSERT_EQ(vt, decoded.type);++ ASSERT_TRUE(!ParseInternalKey(Slice("bar"), &decoded));+}++class FormatTest { };++TEST(FormatTest, InternalKey_EncodeDecode) {+ const char* keys[] = { "", "k", "hello", "longggggggggggggggggggggg" };+ const uint64_t seq[] = {+ 1, 2, 3,+ (1ull << 8) - 1, 1ull << 8, (1ull << 8) + 1,+ (1ull << 16) - 1, 1ull << 16, (1ull << 16) + 1,+ (1ull << 32) - 1, 1ull << 32, (1ull << 32) + 1+ };+ for (int k = 0; k < sizeof(keys) / sizeof(keys[0]); k++) {+ for (int s = 0; s < sizeof(seq) / sizeof(seq[0]); s++) {+ TestKey(keys[k], seq[s], kTypeValue);+ TestKey("hello", 1, kTypeDeletion);+ }+ }+}++TEST(FormatTest, InternalKeyShortSeparator) {+ // When user keys are same+ ASSERT_EQ(IKey("foo", 100, kTypeValue),+ Shorten(IKey("foo", 100, kTypeValue),+ IKey("foo", 99, kTypeValue)));+ ASSERT_EQ(IKey("foo", 100, kTypeValue),+ Shorten(IKey("foo", 100, kTypeValue),+ IKey("foo", 101, kTypeValue)));+ ASSERT_EQ(IKey("foo", 100, kTypeValue),+ Shorten(IKey("foo", 100, kTypeValue),+ IKey("foo", 100, kTypeValue)));+ ASSERT_EQ(IKey("foo", 100, kTypeValue),+ Shorten(IKey("foo", 100, kTypeValue),+ IKey("foo", 100, kTypeDeletion)));++ // When user keys are misordered+ ASSERT_EQ(IKey("foo", 100, kTypeValue),+ Shorten(IKey("foo", 100, kTypeValue),+ IKey("bar", 99, kTypeValue)));++ // When user keys are different, but correctly ordered+ ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),+ Shorten(IKey("foo", 100, kTypeValue),+ IKey("hello", 200, kTypeValue)));++ // When start user key is prefix of limit user key+ ASSERT_EQ(IKey("foo", 100, kTypeValue),+ Shorten(IKey("foo", 100, kTypeValue),+ IKey("foobar", 200, kTypeValue)));++ // When limit user key is prefix of start user key+ ASSERT_EQ(IKey("foobar", 100, kTypeValue),+ Shorten(IKey("foobar", 100, kTypeValue),+ IKey("foo", 200, kTypeValue)));+}++TEST(FormatTest, InternalKeyShortestSuccessor) {+ ASSERT_EQ(IKey("g", kMaxSequenceNumber, kValueTypeForSeek),+ ShortSuccessor(IKey("foo", 100, kTypeValue)));+ ASSERT_EQ(IKey("\xff\xff", 100, kTypeValue),+ ShortSuccessor(IKey("\xff\xff", 100, kTypeValue)));+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/db/filename.cc view
@@ -0,0 +1,139 @@+// 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 <ctype.h>+#include <stdio.h>+#include "db/filename.h"+#include "db/dbformat.h"+#include "leveldb/env.h"+#include "util/logging.h"++namespace leveldb {++// A utility routine: write "data" to the named file and Sync() it.+extern Status WriteStringToFileSync(Env* env, const Slice& data,+ const std::string& fname);++static std::string MakeFileName(const std::string& name, uint64_t number,+ const char* suffix) {+ char buf[100];+ snprintf(buf, sizeof(buf), "/%06llu.%s",+ static_cast<unsigned long long>(number),+ suffix);+ return name + buf;+}++std::string LogFileName(const std::string& name, uint64_t number) {+ assert(number > 0);+ return MakeFileName(name, number, "log");+}++std::string TableFileName(const std::string& name, uint64_t number) {+ assert(number > 0);+ return MakeFileName(name, number, "sst");+}++std::string DescriptorFileName(const std::string& dbname, uint64_t number) {+ assert(number > 0);+ char buf[100];+ snprintf(buf, sizeof(buf), "/MANIFEST-%06llu",+ static_cast<unsigned long long>(number));+ return dbname + buf;+}++std::string CurrentFileName(const std::string& dbname) {+ return dbname + "/CURRENT";+}++std::string LockFileName(const std::string& dbname) {+ return dbname + "/LOCK";+}++std::string TempFileName(const std::string& dbname, uint64_t number) {+ assert(number > 0);+ return MakeFileName(dbname, number, "dbtmp");+}++std::string InfoLogFileName(const std::string& dbname) {+ return dbname + "/LOG";+}++// Return the name of the old info log file for "dbname".+std::string OldInfoLogFileName(const std::string& dbname) {+ return dbname + "/LOG.old";+}+++// Owned filenames have the form:+// dbname/CURRENT+// dbname/LOCK+// dbname/LOG+// dbname/LOG.old+// dbname/MANIFEST-[0-9]++// dbname/[0-9]+.(log|sst)+bool ParseFileName(const std::string& fname,+ uint64_t* number,+ FileType* type) {+ Slice rest(fname);+ if (rest == "CURRENT") {+ *number = 0;+ *type = kCurrentFile;+ } else if (rest == "LOCK") {+ *number = 0;+ *type = kDBLockFile;+ } else if (rest == "LOG" || rest == "LOG.old") {+ *number = 0;+ *type = kInfoLogFile;+ } else if (rest.starts_with("MANIFEST-")) {+ rest.remove_prefix(strlen("MANIFEST-"));+ uint64_t num;+ if (!ConsumeDecimalNumber(&rest, &num)) {+ return false;+ }+ if (!rest.empty()) {+ return false;+ }+ *type = kDescriptorFile;+ *number = num;+ } else {+ // Avoid strtoull() to keep filename format independent of the+ // current locale+ uint64_t num;+ if (!ConsumeDecimalNumber(&rest, &num)) {+ return false;+ }+ Slice suffix = rest;+ if (suffix == Slice(".log")) {+ *type = kLogFile;+ } else if (suffix == Slice(".sst")) {+ *type = kTableFile;+ } else if (suffix == Slice(".dbtmp")) {+ *type = kTempFile;+ } else {+ return false;+ }+ *number = num;+ }+ return true;+}++Status SetCurrentFile(Env* env, const std::string& dbname,+ uint64_t descriptor_number) {+ // Remove leading "dbname/" and add newline to manifest file name+ std::string manifest = DescriptorFileName(dbname, descriptor_number);+ Slice contents = manifest;+ assert(contents.starts_with(dbname + "/"));+ contents.remove_prefix(dbname.size() + 1);+ std::string tmp = TempFileName(dbname, descriptor_number);+ Status s = WriteStringToFileSync(env, contents.ToString() + "\n", tmp);+ if (s.ok()) {+ s = env->RenameFile(tmp, CurrentFileName(dbname));+ }+ if (!s.ok()) {+ env->DeleteFile(tmp);+ }+ return s;+}++} // namespace leveldb
+ cbits/leveldb/db/filename.h view
@@ -0,0 +1,80 @@+// 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.+//+// File names used by DB code++#ifndef STORAGE_LEVELDB_DB_FILENAME_H_+#define STORAGE_LEVELDB_DB_FILENAME_H_++#include <stdint.h>+#include <string>+#include "leveldb/slice.h"+#include "leveldb/status.h"+#include "port/port.h"++namespace leveldb {++class Env;++enum FileType {+ kLogFile,+ kDBLockFile,+ kTableFile,+ kDescriptorFile,+ kCurrentFile,+ kTempFile,+ kInfoLogFile // Either the current one, or an old one+};++// Return the name of the log file with the specified number+// in the db named by "dbname". The result will be prefixed with+// "dbname".+extern std::string LogFileName(const std::string& dbname, uint64_t number);++// Return the name of the sstable with the specified number+// in the db named by "dbname". The result will be prefixed with+// "dbname".+extern std::string TableFileName(const std::string& dbname, uint64_t number);++// Return the name of the descriptor file for the db named by+// "dbname" and the specified incarnation number. The result will be+// prefixed with "dbname".+extern std::string DescriptorFileName(const std::string& dbname,+ uint64_t number);++// Return the name of the current file. This file contains the name+// of the current manifest file. The result will be prefixed with+// "dbname".+extern std::string CurrentFileName(const std::string& dbname);++// Return the name of the lock file for the db named by+// "dbname". The result will be prefixed with "dbname".+extern std::string LockFileName(const std::string& dbname);++// Return the name of a temporary file owned by the db named "dbname".+// The result will be prefixed with "dbname".+extern std::string TempFileName(const std::string& dbname, uint64_t number);++// Return the name of the info log file for "dbname".+extern std::string InfoLogFileName(const std::string& dbname);++// Return the name of the old info log file for "dbname".+extern std::string OldInfoLogFileName(const std::string& dbname);++// If filename is a leveldb file, store the type of the file in *type.+// The number encoded in the filename is stored in *number. If the+// filename was successfully parsed, returns true. Else return false.+extern bool ParseFileName(const std::string& filename,+ uint64_t* number,+ FileType* type);++// Make the CURRENT file point to the descriptor file with the+// specified number.+extern Status SetCurrentFile(Env* env, const std::string& dbname,+ uint64_t descriptor_number);+++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_FILENAME_H_
+ cbits/leveldb/db/filename_test.cc view
@@ -0,0 +1,122 @@+// 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 "db/filename.h"++#include "db/dbformat.h"+#include "port/port.h"+#include "util/logging.h"+#include "util/testharness.h"++namespace leveldb {++class FileNameTest { };++TEST(FileNameTest, Parse) {+ Slice db;+ FileType type;+ uint64_t number;++ // Successful parses+ static struct {+ const char* fname;+ uint64_t number;+ FileType type;+ } cases[] = {+ { "100.log", 100, kLogFile },+ { "0.log", 0, kLogFile },+ { "0.sst", 0, kTableFile },+ { "CURRENT", 0, kCurrentFile },+ { "LOCK", 0, kDBLockFile },+ { "MANIFEST-2", 2, kDescriptorFile },+ { "MANIFEST-7", 7, kDescriptorFile },+ { "LOG", 0, kInfoLogFile },+ { "LOG.old", 0, kInfoLogFile },+ { "18446744073709551615.log", 18446744073709551615ull, kLogFile },+ };+ for (int i = 0; i < sizeof(cases) / sizeof(cases[0]); i++) {+ std::string f = cases[i].fname;+ ASSERT_TRUE(ParseFileName(f, &number, &type)) << f;+ ASSERT_EQ(cases[i].type, type) << f;+ ASSERT_EQ(cases[i].number, number) << f;+ }++ // Errors+ static const char* errors[] = {+ "",+ "foo",+ "foo-dx-100.log",+ ".log",+ "",+ "manifest",+ "CURREN",+ "CURRENTX",+ "MANIFES",+ "MANIFEST",+ "MANIFEST-",+ "XMANIFEST-3",+ "MANIFEST-3x",+ "LOC",+ "LOCKx",+ "LO",+ "LOGx",+ "18446744073709551616.log",+ "184467440737095516150.log",+ "100",+ "100.",+ "100.lop"+ };+ for (int i = 0; i < sizeof(errors) / sizeof(errors[0]); i++) {+ std::string f = errors[i];+ ASSERT_TRUE(!ParseFileName(f, &number, &type)) << f;+ };+}++TEST(FileNameTest, Construction) {+ uint64_t number;+ FileType type;+ std::string fname;++ fname = CurrentFileName("foo");+ ASSERT_EQ("foo/", std::string(fname.data(), 4));+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));+ ASSERT_EQ(0, number);+ ASSERT_EQ(kCurrentFile, type);++ fname = LockFileName("foo");+ ASSERT_EQ("foo/", std::string(fname.data(), 4));+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));+ ASSERT_EQ(0, number);+ ASSERT_EQ(kDBLockFile, type);++ fname = LogFileName("foo", 192);+ ASSERT_EQ("foo/", std::string(fname.data(), 4));+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));+ ASSERT_EQ(192, number);+ ASSERT_EQ(kLogFile, type);++ fname = TableFileName("bar", 200);+ ASSERT_EQ("bar/", std::string(fname.data(), 4));+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));+ ASSERT_EQ(200, number);+ ASSERT_EQ(kTableFile, type);++ fname = DescriptorFileName("bar", 100);+ ASSERT_EQ("bar/", std::string(fname.data(), 4));+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));+ ASSERT_EQ(100, number);+ ASSERT_EQ(kDescriptorFile, type);++ fname = TempFileName("tmp", 999);+ ASSERT_EQ("tmp/", std::string(fname.data(), 4));+ ASSERT_TRUE(ParseFileName(fname.c_str() + 4, &number, &type));+ ASSERT_EQ(999, number);+ ASSERT_EQ(kTempFile, type);+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/db/log_format.h view
@@ -0,0 +1,35 @@+// 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.+//+// Log format information shared by reader and writer.+// See ../doc/log_format.txt for more detail.++#ifndef STORAGE_LEVELDB_DB_LOG_FORMAT_H_+#define STORAGE_LEVELDB_DB_LOG_FORMAT_H_++namespace leveldb {+namespace log {++enum RecordType {+ // Zero is reserved for preallocated files+ kZeroType = 0,++ kFullType = 1,++ // For fragments+ kFirstType = 2,+ kMiddleType = 3,+ kLastType = 4+};+static const int kMaxRecordType = kLastType;++static const int kBlockSize = 32768;++// Header is checksum (4 bytes), type (1 byte), length (2 bytes).+static const int kHeaderSize = 4 + 1 + 2;++} // namespace log+} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_
+ cbits/leveldb/db/log_reader.cc view
@@ -0,0 +1,259 @@+// 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 "db/log_reader.h"++#include <stdio.h>+#include "leveldb/env.h"+#include "util/coding.h"+#include "util/crc32c.h"++namespace leveldb {+namespace log {++Reader::Reporter::~Reporter() {+}++Reader::Reader(SequentialFile* file, Reporter* reporter, bool checksum,+ uint64_t initial_offset)+ : file_(file),+ reporter_(reporter),+ checksum_(checksum),+ backing_store_(new char[kBlockSize]),+ buffer_(),+ eof_(false),+ last_record_offset_(0),+ end_of_buffer_offset_(0),+ initial_offset_(initial_offset) {+}++Reader::~Reader() {+ delete[] backing_store_;+}++bool Reader::SkipToInitialBlock() {+ size_t offset_in_block = initial_offset_ % kBlockSize;+ uint64_t block_start_location = initial_offset_ - offset_in_block;++ // Don't search a block if we'd be in the trailer+ if (offset_in_block > kBlockSize - 6) {+ offset_in_block = 0;+ block_start_location += kBlockSize;+ }++ end_of_buffer_offset_ = block_start_location;++ // Skip to start of first block that can contain the initial record+ if (block_start_location > 0) {+ Status skip_status = file_->Skip(block_start_location);+ if (!skip_status.ok()) {+ ReportDrop(block_start_location, skip_status);+ return false;+ }+ }++ return true;+}++bool Reader::ReadRecord(Slice* record, std::string* scratch) {+ if (last_record_offset_ < initial_offset_) {+ if (!SkipToInitialBlock()) {+ return false;+ }+ }++ scratch->clear();+ record->clear();+ bool in_fragmented_record = false;+ // Record offset of the logical record that we're reading+ // 0 is a dummy value to make compilers happy+ uint64_t prospective_record_offset = 0;++ Slice fragment;+ while (true) {+ uint64_t physical_record_offset = end_of_buffer_offset_ - buffer_.size();+ const unsigned int record_type = ReadPhysicalRecord(&fragment);+ switch (record_type) {+ case kFullType:+ if (in_fragmented_record) {+ // Handle bug in earlier versions of log::Writer where+ // it could emit an empty kFirstType record at the tail end+ // of a block followed by a kFullType or kFirstType record+ // at the beginning of the next block.+ if (scratch->empty()) {+ in_fragmented_record = false;+ } else {+ ReportCorruption(scratch->size(), "partial record without end(1)");+ }+ }+ prospective_record_offset = physical_record_offset;+ scratch->clear();+ *record = fragment;+ last_record_offset_ = prospective_record_offset;+ return true;++ case kFirstType:+ if (in_fragmented_record) {+ // Handle bug in earlier versions of log::Writer where+ // it could emit an empty kFirstType record at the tail end+ // of a block followed by a kFullType or kFirstType record+ // at the beginning of the next block.+ if (scratch->empty()) {+ in_fragmented_record = false;+ } else {+ ReportCorruption(scratch->size(), "partial record without end(2)");+ }+ }+ prospective_record_offset = physical_record_offset;+ scratch->assign(fragment.data(), fragment.size());+ in_fragmented_record = true;+ break;++ case kMiddleType:+ if (!in_fragmented_record) {+ ReportCorruption(fragment.size(),+ "missing start of fragmented record(1)");+ } else {+ scratch->append(fragment.data(), fragment.size());+ }+ break;++ case kLastType:+ if (!in_fragmented_record) {+ ReportCorruption(fragment.size(),+ "missing start of fragmented record(2)");+ } else {+ scratch->append(fragment.data(), fragment.size());+ *record = Slice(*scratch);+ last_record_offset_ = prospective_record_offset;+ return true;+ }+ break;++ case kEof:+ if (in_fragmented_record) {+ ReportCorruption(scratch->size(), "partial record without end(3)");+ scratch->clear();+ }+ return false;++ case kBadRecord:+ if (in_fragmented_record) {+ ReportCorruption(scratch->size(), "error in middle of record");+ in_fragmented_record = false;+ scratch->clear();+ }+ break;++ default: {+ char buf[40];+ snprintf(buf, sizeof(buf), "unknown record type %u", record_type);+ ReportCorruption(+ (fragment.size() + (in_fragmented_record ? scratch->size() : 0)),+ buf);+ in_fragmented_record = false;+ scratch->clear();+ break;+ }+ }+ }+ return false;+}++uint64_t Reader::LastRecordOffset() {+ return last_record_offset_;+}++void Reader::ReportCorruption(size_t bytes, const char* reason) {+ ReportDrop(bytes, Status::Corruption(reason));+}++void Reader::ReportDrop(size_t bytes, const Status& reason) {+ if (reporter_ != NULL &&+ end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) {+ reporter_->Corruption(bytes, reason);+ }+}++unsigned int Reader::ReadPhysicalRecord(Slice* result) {+ while (true) {+ if (buffer_.size() < kHeaderSize) {+ if (!eof_) {+ // Last read was a full read, so this is a trailer to skip+ buffer_.clear();+ Status status = file_->Read(kBlockSize, &buffer_, backing_store_);+ end_of_buffer_offset_ += buffer_.size();+ if (!status.ok()) {+ buffer_.clear();+ ReportDrop(kBlockSize, status);+ eof_ = true;+ return kEof;+ } else if (buffer_.size() < kBlockSize) {+ eof_ = true;+ }+ continue;+ } else if (buffer_.size() == 0) {+ // End of file+ return kEof;+ } else {+ size_t drop_size = buffer_.size();+ buffer_.clear();+ ReportCorruption(drop_size, "truncated record at end of file");+ return kEof;+ }+ }++ // Parse the header+ const char* header = buffer_.data();+ const uint32_t a = static_cast<uint32_t>(header[4]) & 0xff;+ const uint32_t b = static_cast<uint32_t>(header[5]) & 0xff;+ const unsigned int type = header[6];+ const uint32_t length = a | (b << 8);+ if (kHeaderSize + length > buffer_.size()) {+ size_t drop_size = buffer_.size();+ buffer_.clear();+ ReportCorruption(drop_size, "bad record length");+ return kBadRecord;+ }++ if (type == kZeroType && length == 0) {+ // Skip zero length record without reporting any drops since+ // such records are produced by the mmap based writing code in+ // env_posix.cc that preallocates file regions.+ buffer_.clear();+ return kBadRecord;+ }++ // Check crc+ if (checksum_) {+ uint32_t expected_crc = crc32c::Unmask(DecodeFixed32(header));+ uint32_t actual_crc = crc32c::Value(header + 6, 1 + length);+ if (actual_crc != expected_crc) {+ // Drop the rest of the buffer since "length" itself may have+ // been corrupted and if we trust it, we could find some+ // fragment of a real log record that just happens to look+ // like a valid log record.+ size_t drop_size = buffer_.size();+ buffer_.clear();+ ReportCorruption(drop_size, "checksum mismatch");+ return kBadRecord;+ }+ }++ buffer_.remove_prefix(kHeaderSize + length);++ // Skip physical record that started before initial_offset_+ if (end_of_buffer_offset_ - buffer_.size() - kHeaderSize - length <+ initial_offset_) {+ result->clear();+ return kBadRecord;+ }++ *result = Slice(header + kHeaderSize, length);+ return type;+ }+}++} // namespace log+} // namespace leveldb
+ cbits/leveldb/db/log_reader.h view
@@ -0,0 +1,108 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_+#define STORAGE_LEVELDB_DB_LOG_READER_H_++#include <stdint.h>++#include "db/log_format.h"+#include "leveldb/slice.h"+#include "leveldb/status.h"++namespace leveldb {++class SequentialFile;++namespace log {++class Reader {+ public:+ // Interface for reporting errors.+ class Reporter {+ public:+ virtual ~Reporter();++ // Some corruption was detected. "size" is the approximate number+ // of bytes dropped due to the corruption.+ virtual void Corruption(size_t bytes, const Status& status) = 0;+ };++ // Create a reader that will return log records from "*file".+ // "*file" must remain live while this Reader is in use.+ //+ // If "reporter" is non-NULL, it is notified whenever some data is+ // dropped due to a detected corruption. "*reporter" must remain+ // live while this Reader is in use.+ //+ // If "checksum" is true, verify checksums if available.+ //+ // The Reader will start reading at the first record located at physical+ // position >= initial_offset within the file.+ Reader(SequentialFile* file, Reporter* reporter, bool checksum,+ uint64_t initial_offset);++ ~Reader();++ // Read the next record into *record. Returns true if read+ // successfully, false if we hit end of the input. May use+ // "*scratch" as temporary storage. The contents filled in *record+ // will only be valid until the next mutating operation on this+ // reader or the next mutation to *scratch.+ bool ReadRecord(Slice* record, std::string* scratch);++ // Returns the physical offset of the last record returned by ReadRecord.+ //+ // Undefined before the first call to ReadRecord.+ uint64_t LastRecordOffset();++ private:+ SequentialFile* const file_;+ Reporter* const reporter_;+ bool const checksum_;+ char* const backing_store_;+ Slice buffer_;+ bool eof_; // Last Read() indicated EOF by returning < kBlockSize++ // Offset of the last record returned by ReadRecord.+ uint64_t last_record_offset_;+ // Offset of the first location past the end of buffer_.+ uint64_t end_of_buffer_offset_;++ // Offset at which to start looking for the first record to return+ uint64_t const initial_offset_;++ // Extend record types with the following special values+ enum {+ kEof = kMaxRecordType + 1,+ // Returned whenever we find an invalid physical record.+ // Currently there are three situations in which this happens:+ // * The record has an invalid CRC (ReadPhysicalRecord reports a drop)+ // * The record is a 0-length record (No drop is reported)+ // * The record is below constructor's initial_offset (No drop is reported)+ kBadRecord = kMaxRecordType + 2+ };++ // Skips all blocks that are completely before "initial_offset_".+ //+ // Returns true on success. Handles reporting.+ bool SkipToInitialBlock();++ // Return type, or one of the preceding special values+ unsigned int ReadPhysicalRecord(Slice* result);++ // Reports dropped bytes to the reporter.+ // buffer_ must be updated to remove the dropped bytes prior to invocation.+ void ReportCorruption(size_t bytes, const char* reason);+ void ReportDrop(size_t bytes, const Status& reason);++ // No copying allowed+ Reader(const Reader&);+ void operator=(const Reader&);+};++} // namespace log+} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_LOG_READER_H_
+ cbits/leveldb/db/log_test.cc view
@@ -0,0 +1,500 @@+// 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 "db/log_reader.h"+#include "db/log_writer.h"+#include "leveldb/env.h"+#include "util/coding.h"+#include "util/crc32c.h"+#include "util/random.h"+#include "util/testharness.h"++namespace leveldb {+namespace log {++// Construct a string of the specified length made out of the supplied+// partial string.+static std::string BigString(const std::string& partial_string, size_t n) {+ std::string result;+ while (result.size() < n) {+ result.append(partial_string);+ }+ result.resize(n);+ return result;+}++// Construct a string from a number+static std::string NumberString(int n) {+ char buf[50];+ snprintf(buf, sizeof(buf), "%d.", n);+ return std::string(buf);+}++// Return a skewed potentially long string+static std::string RandomSkewedString(int i, Random* rnd) {+ return BigString(NumberString(i), rnd->Skewed(17));+}++class LogTest {+ private:+ class StringDest : public WritableFile {+ public:+ std::string contents_;++ virtual Status Close() { return Status::OK(); }+ virtual Status Flush() { return Status::OK(); }+ virtual Status Sync() { return Status::OK(); }+ virtual Status Append(const Slice& slice) {+ contents_.append(slice.data(), slice.size());+ return Status::OK();+ }+ };++ class StringSource : public SequentialFile {+ public:+ Slice contents_;+ bool force_error_;+ bool returned_partial_;+ StringSource() : force_error_(false), returned_partial_(false) { }++ virtual Status Read(size_t n, Slice* result, char* scratch) {+ ASSERT_TRUE(!returned_partial_) << "must not Read() after eof/error";++ if (force_error_) {+ force_error_ = false;+ returned_partial_ = true;+ return Status::Corruption("read error");+ }++ if (contents_.size() < n) {+ n = contents_.size();+ returned_partial_ = true;+ }+ *result = Slice(contents_.data(), n);+ contents_.remove_prefix(n);+ return Status::OK();+ }++ virtual Status Skip(uint64_t n) {+ if (n > contents_.size()) {+ contents_.clear();+ return Status::NotFound("in-memory file skipepd past end");+ }++ contents_.remove_prefix(n);++ return Status::OK();+ }+ };++ class ReportCollector : public Reader::Reporter {+ public:+ size_t dropped_bytes_;+ std::string message_;++ ReportCollector() : dropped_bytes_(0) { }+ virtual void Corruption(size_t bytes, const Status& status) {+ dropped_bytes_ += bytes;+ message_.append(status.ToString());+ }+ };++ StringDest dest_;+ StringSource source_;+ ReportCollector report_;+ bool reading_;+ Writer writer_;+ Reader reader_;++ // Record metadata for testing initial offset functionality+ static size_t initial_offset_record_sizes_[];+ static uint64_t initial_offset_last_record_offsets_[];++ public:+ LogTest() : reading_(false),+ writer_(&dest_),+ reader_(&source_, &report_, true/*checksum*/,+ 0/*initial_offset*/) {+ }++ void Write(const std::string& msg) {+ ASSERT_TRUE(!reading_) << "Write() after starting to read";+ writer_.AddRecord(Slice(msg));+ }++ size_t WrittenBytes() const {+ return dest_.contents_.size();+ }++ std::string Read() {+ if (!reading_) {+ reading_ = true;+ source_.contents_ = Slice(dest_.contents_);+ }+ std::string scratch;+ Slice record;+ if (reader_.ReadRecord(&record, &scratch)) {+ return record.ToString();+ } else {+ return "EOF";+ }+ }++ void IncrementByte(int offset, int delta) {+ dest_.contents_[offset] += delta;+ }++ void SetByte(int offset, char new_byte) {+ dest_.contents_[offset] = new_byte;+ }++ void ShrinkSize(int bytes) {+ dest_.contents_.resize(dest_.contents_.size() - bytes);+ }++ void FixChecksum(int header_offset, int len) {+ // Compute crc of type/len/data+ uint32_t crc = crc32c::Value(&dest_.contents_[header_offset+6], 1 + len);+ crc = crc32c::Mask(crc);+ EncodeFixed32(&dest_.contents_[header_offset], crc);+ }++ void ForceError() {+ source_.force_error_ = true;+ }++ size_t DroppedBytes() const {+ return report_.dropped_bytes_;+ }++ std::string ReportMessage() const {+ return report_.message_;+ }++ // Returns OK iff recorded error message contains "msg"+ std::string MatchError(const std::string& msg) const {+ if (report_.message_.find(msg) == std::string::npos) {+ return report_.message_;+ } else {+ return "OK";+ }+ }++ void WriteInitialOffsetLog() {+ for (int i = 0; i < 4; i++) {+ std::string record(initial_offset_record_sizes_[i],+ static_cast<char>('a' + i));+ Write(record);+ }+ }++ void CheckOffsetPastEndReturnsNoRecords(uint64_t offset_past_end) {+ WriteInitialOffsetLog();+ reading_ = true;+ source_.contents_ = Slice(dest_.contents_);+ Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/,+ WrittenBytes() + offset_past_end);+ Slice record;+ std::string scratch;+ ASSERT_TRUE(!offset_reader->ReadRecord(&record, &scratch));+ delete offset_reader;+ }++ void CheckInitialOffsetRecord(uint64_t initial_offset,+ int expected_record_offset) {+ WriteInitialOffsetLog();+ reading_ = true;+ source_.contents_ = Slice(dest_.contents_);+ Reader* offset_reader = new Reader(&source_, &report_, true/*checksum*/,+ initial_offset);+ Slice record;+ std::string scratch;+ ASSERT_TRUE(offset_reader->ReadRecord(&record, &scratch));+ ASSERT_EQ(initial_offset_record_sizes_[expected_record_offset],+ record.size());+ ASSERT_EQ(initial_offset_last_record_offsets_[expected_record_offset],+ offset_reader->LastRecordOffset());+ ASSERT_EQ((char)('a' + expected_record_offset), record.data()[0]);+ delete offset_reader;+ }++};++size_t LogTest::initial_offset_record_sizes_[] =+ {10000, // Two sizable records in first block+ 10000,+ 2 * log::kBlockSize - 1000, // Span three blocks+ 1};++uint64_t LogTest::initial_offset_last_record_offsets_[] =+ {0,+ kHeaderSize + 10000,+ 2 * (kHeaderSize + 10000),+ 2 * (kHeaderSize + 10000) ++ (2 * log::kBlockSize - 1000) + 3 * kHeaderSize};+++TEST(LogTest, Empty) {+ ASSERT_EQ("EOF", Read());+}++TEST(LogTest, ReadWrite) {+ Write("foo");+ Write("bar");+ Write("");+ Write("xxxx");+ ASSERT_EQ("foo", Read());+ ASSERT_EQ("bar", Read());+ ASSERT_EQ("", Read());+ ASSERT_EQ("xxxx", Read());+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ("EOF", Read()); // Make sure reads at eof work+}++TEST(LogTest, ManyBlocks) {+ for (int i = 0; i < 100000; i++) {+ Write(NumberString(i));+ }+ for (int i = 0; i < 100000; i++) {+ ASSERT_EQ(NumberString(i), Read());+ }+ ASSERT_EQ("EOF", Read());+}++TEST(LogTest, Fragmentation) {+ Write("small");+ Write(BigString("medium", 50000));+ Write(BigString("large", 100000));+ ASSERT_EQ("small", Read());+ ASSERT_EQ(BigString("medium", 50000), Read());+ ASSERT_EQ(BigString("large", 100000), Read());+ ASSERT_EQ("EOF", Read());+}++TEST(LogTest, MarginalTrailer) {+ // Make a trailer that is exactly the same length as an empty record.+ const int n = kBlockSize - 2*kHeaderSize;+ Write(BigString("foo", n));+ ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes());+ Write("");+ Write("bar");+ ASSERT_EQ(BigString("foo", n), Read());+ ASSERT_EQ("", Read());+ ASSERT_EQ("bar", Read());+ ASSERT_EQ("EOF", Read());+}++TEST(LogTest, MarginalTrailer2) {+ // Make a trailer that is exactly the same length as an empty record.+ const int n = kBlockSize - 2*kHeaderSize;+ Write(BigString("foo", n));+ ASSERT_EQ(kBlockSize - kHeaderSize, WrittenBytes());+ Write("bar");+ ASSERT_EQ(BigString("foo", n), Read());+ ASSERT_EQ("bar", Read());+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(0, DroppedBytes());+ ASSERT_EQ("", ReportMessage());+}++TEST(LogTest, ShortTrailer) {+ const int n = kBlockSize - 2*kHeaderSize + 4;+ Write(BigString("foo", n));+ ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());+ Write("");+ Write("bar");+ ASSERT_EQ(BigString("foo", n), Read());+ ASSERT_EQ("", Read());+ ASSERT_EQ("bar", Read());+ ASSERT_EQ("EOF", Read());+}++TEST(LogTest, AlignedEof) {+ const int n = kBlockSize - 2*kHeaderSize + 4;+ Write(BigString("foo", n));+ ASSERT_EQ(kBlockSize - kHeaderSize + 4, WrittenBytes());+ ASSERT_EQ(BigString("foo", n), Read());+ ASSERT_EQ("EOF", Read());+}++TEST(LogTest, RandomRead) {+ const int N = 500;+ Random write_rnd(301);+ for (int i = 0; i < N; i++) {+ Write(RandomSkewedString(i, &write_rnd));+ }+ Random read_rnd(301);+ for (int i = 0; i < N; i++) {+ ASSERT_EQ(RandomSkewedString(i, &read_rnd), Read());+ }+ ASSERT_EQ("EOF", Read());+}++// Tests of all the error paths in log_reader.cc follow:++TEST(LogTest, ReadError) {+ Write("foo");+ ForceError();+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(kBlockSize, DroppedBytes());+ ASSERT_EQ("OK", MatchError("read error"));+}++TEST(LogTest, BadRecordType) {+ Write("foo");+ // Type is stored in header[6]+ IncrementByte(6, 100);+ FixChecksum(0, 3);+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(3, DroppedBytes());+ ASSERT_EQ("OK", MatchError("unknown record type"));+}++TEST(LogTest, TruncatedTrailingRecord) {+ Write("foo");+ ShrinkSize(4); // Drop all payload as well as a header byte+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(kHeaderSize - 1, DroppedBytes());+ ASSERT_EQ("OK", MatchError("truncated record at end of file"));+}++TEST(LogTest, BadLength) {+ Write("foo");+ ShrinkSize(1);+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(kHeaderSize + 2, DroppedBytes());+ ASSERT_EQ("OK", MatchError("bad record length"));+}++TEST(LogTest, ChecksumMismatch) {+ Write("foo");+ IncrementByte(0, 10);+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(10, DroppedBytes());+ ASSERT_EQ("OK", MatchError("checksum mismatch"));+}++TEST(LogTest, UnexpectedMiddleType) {+ Write("foo");+ SetByte(6, kMiddleType);+ FixChecksum(0, 3);+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(3, DroppedBytes());+ ASSERT_EQ("OK", MatchError("missing start"));+}++TEST(LogTest, UnexpectedLastType) {+ Write("foo");+ SetByte(6, kLastType);+ FixChecksum(0, 3);+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(3, DroppedBytes());+ ASSERT_EQ("OK", MatchError("missing start"));+}++TEST(LogTest, UnexpectedFullType) {+ Write("foo");+ Write("bar");+ SetByte(6, kFirstType);+ FixChecksum(0, 3);+ ASSERT_EQ("bar", Read());+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(3, DroppedBytes());+ ASSERT_EQ("OK", MatchError("partial record without end"));+}++TEST(LogTest, UnexpectedFirstType) {+ Write("foo");+ Write(BigString("bar", 100000));+ SetByte(6, kFirstType);+ FixChecksum(0, 3);+ ASSERT_EQ(BigString("bar", 100000), Read());+ ASSERT_EQ("EOF", Read());+ ASSERT_EQ(3, DroppedBytes());+ ASSERT_EQ("OK", MatchError("partial record without end"));+}++TEST(LogTest, ErrorJoinsRecords) {+ // Consider two fragmented records:+ // first(R1) last(R1) first(R2) last(R2)+ // where the middle two fragments disappear. We do not want+ // first(R1),last(R2) to get joined and returned as a valid record.++ // Write records that span two blocks+ Write(BigString("foo", kBlockSize));+ Write(BigString("bar", kBlockSize));+ Write("correct");++ // Wipe the middle block+ for (int offset = kBlockSize; offset < 2*kBlockSize; offset++) {+ SetByte(offset, 'x');+ }++ ASSERT_EQ("correct", Read());+ ASSERT_EQ("EOF", Read());+ const int dropped = DroppedBytes();+ ASSERT_LE(dropped, 2*kBlockSize + 100);+ ASSERT_GE(dropped, 2*kBlockSize);+}++TEST(LogTest, ReadStart) {+ CheckInitialOffsetRecord(0, 0);+}++TEST(LogTest, ReadSecondOneOff) {+ CheckInitialOffsetRecord(1, 1);+}++TEST(LogTest, ReadSecondTenThousand) {+ CheckInitialOffsetRecord(10000, 1);+}++TEST(LogTest, ReadSecondStart) {+ CheckInitialOffsetRecord(10007, 1);+}++TEST(LogTest, ReadThirdOneOff) {+ CheckInitialOffsetRecord(10008, 2);+}++TEST(LogTest, ReadThirdStart) {+ CheckInitialOffsetRecord(20014, 2);+}++TEST(LogTest, ReadFourthOneOff) {+ CheckInitialOffsetRecord(20015, 3);+}++TEST(LogTest, ReadFourthFirstBlockTrailer) {+ CheckInitialOffsetRecord(log::kBlockSize - 4, 3);+}++TEST(LogTest, ReadFourthMiddleBlock) {+ CheckInitialOffsetRecord(log::kBlockSize + 1, 3);+}++TEST(LogTest, ReadFourthLastBlock) {+ CheckInitialOffsetRecord(2 * log::kBlockSize + 1, 3);+}++TEST(LogTest, ReadFourthStart) {+ CheckInitialOffsetRecord(+ 2 * (kHeaderSize + 1000) + (2 * log::kBlockSize - 1000) + 3 * kHeaderSize,+ 3);+}++TEST(LogTest, ReadEnd) {+ CheckOffsetPastEndReturnsNoRecords(0);+}++TEST(LogTest, ReadPastEnd) {+ CheckOffsetPastEndReturnsNoRecords(5);+}++} // namespace log+} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/db/log_writer.cc view
@@ -0,0 +1,103 @@+// 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 "db/log_writer.h"++#include <stdint.h>+#include "leveldb/env.h"+#include "util/coding.h"+#include "util/crc32c.h"++namespace leveldb {+namespace log {++Writer::Writer(WritableFile* dest)+ : dest_(dest),+ block_offset_(0) {+ for (int i = 0; i <= kMaxRecordType; i++) {+ char t = static_cast<char>(i);+ type_crc_[i] = crc32c::Value(&t, 1);+ }+}++Writer::~Writer() {+}++Status Writer::AddRecord(const Slice& slice) {+ const char* ptr = slice.data();+ size_t left = slice.size();++ // Fragment the record if necessary and emit it. Note that if slice+ // is empty, we still want to iterate once to emit a single+ // zero-length record+ Status s;+ bool begin = true;+ do {+ const int leftover = kBlockSize - block_offset_;+ assert(leftover >= 0);+ if (leftover < kHeaderSize) {+ // Switch to a new block+ if (leftover > 0) {+ // Fill the trailer (literal below relies on kHeaderSize being 7)+ assert(kHeaderSize == 7);+ dest_->Append(Slice("\x00\x00\x00\x00\x00\x00", leftover));+ }+ block_offset_ = 0;+ }++ // Invariant: we never leave < kHeaderSize bytes in a block.+ assert(kBlockSize - block_offset_ - kHeaderSize >= 0);++ const size_t avail = kBlockSize - block_offset_ - kHeaderSize;+ const size_t fragment_length = (left < avail) ? left : avail;++ RecordType type;+ const bool end = (left == fragment_length);+ if (begin && end) {+ type = kFullType;+ } else if (begin) {+ type = kFirstType;+ } else if (end) {+ type = kLastType;+ } else {+ type = kMiddleType;+ }++ s = EmitPhysicalRecord(type, ptr, fragment_length);+ ptr += fragment_length;+ left -= fragment_length;+ begin = false;+ } while (s.ok() && left > 0);+ return s;+}++Status Writer::EmitPhysicalRecord(RecordType t, const char* ptr, size_t n) {+ assert(n <= 0xffff); // Must fit in two bytes+ assert(block_offset_ + kHeaderSize + n <= kBlockSize);++ // Format the header+ char buf[kHeaderSize];+ buf[4] = static_cast<char>(n & 0xff);+ buf[5] = static_cast<char>(n >> 8);+ buf[6] = static_cast<char>(t);++ // Compute the crc of the record type and the payload.+ uint32_t crc = crc32c::Extend(type_crc_[t], ptr, n);+ crc = crc32c::Mask(crc); // Adjust for storage+ EncodeFixed32(buf, crc);++ // Write the header and the payload+ Status s = dest_->Append(Slice(buf, kHeaderSize));+ if (s.ok()) {+ s = dest_->Append(Slice(ptr, n));+ if (s.ok()) {+ s = dest_->Flush();+ }+ }+ block_offset_ += kHeaderSize + n;+ return s;+}++} // namespace log+} // namespace leveldb
+ cbits/leveldb/db/log_writer.h view
@@ -0,0 +1,48 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_LOG_WRITER_H_+#define STORAGE_LEVELDB_DB_LOG_WRITER_H_++#include <stdint.h>+#include "db/log_format.h"+#include "leveldb/slice.h"+#include "leveldb/status.h"++namespace leveldb {++class WritableFile;++namespace log {++class Writer {+ public:+ // Create a writer that will append data to "*dest".+ // "*dest" must be initially empty.+ // "*dest" must remain live while this Writer is in use.+ explicit Writer(WritableFile* dest);+ ~Writer();++ Status AddRecord(const Slice& slice);++ private:+ WritableFile* dest_;+ int block_offset_; // Current offset in block++ // crc32c values for all supported record types. These are+ // pre-computed to reduce the overhead of computing the crc of the+ // record type stored in the header.+ uint32_t type_crc_[kMaxRecordType + 1];++ Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);++ // No copying allowed+ Writer(const Writer&);+ void operator=(const Writer&);+};++} // namespace log+} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_LOG_WRITER_H_
+ cbits/leveldb/db/memtable.cc view
@@ -0,0 +1,145 @@+// 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 "db/memtable.h"+#include "db/dbformat.h"+#include "leveldb/comparator.h"+#include "leveldb/env.h"+#include "leveldb/iterator.h"+#include "util/coding.h"++namespace leveldb {++static Slice GetLengthPrefixedSlice(const char* data) {+ uint32_t len;+ const char* p = data;+ p = GetVarint32Ptr(p, p + 5, &len); // +5: we assume "p" is not corrupted+ return Slice(p, len);+}++MemTable::MemTable(const InternalKeyComparator& cmp)+ : comparator_(cmp),+ refs_(0),+ table_(comparator_, &arena_) {+}++MemTable::~MemTable() {+ assert(refs_ == 0);+}++size_t MemTable::ApproximateMemoryUsage() { return arena_.MemoryUsage(); }++int MemTable::KeyComparator::operator()(const char* aptr, const char* bptr)+ const {+ // Internal keys are encoded as length-prefixed strings.+ Slice a = GetLengthPrefixedSlice(aptr);+ Slice b = GetLengthPrefixedSlice(bptr);+ return comparator.Compare(a, b);+}++// Encode a suitable internal key target for "target" and return it.+// Uses *scratch as scratch space, and the returned pointer will point+// into this scratch space.+static const char* EncodeKey(std::string* scratch, const Slice& target) {+ scratch->clear();+ PutVarint32(scratch, target.size());+ scratch->append(target.data(), target.size());+ return scratch->data();+}++class MemTableIterator: public Iterator {+ public:+ explicit MemTableIterator(MemTable::Table* table) : iter_(table) { }++ virtual bool Valid() const { return iter_.Valid(); }+ virtual void Seek(const Slice& k) { iter_.Seek(EncodeKey(&tmp_, k)); }+ virtual void SeekToFirst() { iter_.SeekToFirst(); }+ virtual void SeekToLast() { iter_.SeekToLast(); }+ virtual void Next() { iter_.Next(); }+ virtual void Prev() { iter_.Prev(); }+ virtual Slice key() const { return GetLengthPrefixedSlice(iter_.key()); }+ virtual Slice value() const {+ Slice key_slice = GetLengthPrefixedSlice(iter_.key());+ return GetLengthPrefixedSlice(key_slice.data() + key_slice.size());+ }++ virtual Status status() const { return Status::OK(); }++ private:+ MemTable::Table::Iterator iter_;+ std::string tmp_; // For passing to EncodeKey++ // No copying allowed+ MemTableIterator(const MemTableIterator&);+ void operator=(const MemTableIterator&);+};++Iterator* MemTable::NewIterator() {+ return new MemTableIterator(&table_);+}++void MemTable::Add(SequenceNumber s, ValueType type,+ const Slice& key,+ const Slice& value) {+ // Format of an entry is concatenation of:+ // key_size : varint32 of internal_key.size()+ // key bytes : char[internal_key.size()]+ // value_size : varint32 of value.size()+ // value bytes : char[value.size()]+ size_t key_size = key.size();+ size_t val_size = value.size();+ size_t internal_key_size = key_size + 8;+ const size_t encoded_len =+ VarintLength(internal_key_size) + internal_key_size ++ VarintLength(val_size) + val_size;+ char* buf = arena_.Allocate(encoded_len);+ char* p = EncodeVarint32(buf, internal_key_size);+ memcpy(p, key.data(), key_size);+ p += key_size;+ EncodeFixed64(p, (s << 8) | type);+ p += 8;+ p = EncodeVarint32(p, val_size);+ memcpy(p, value.data(), val_size);+ assert((p + val_size) - buf == encoded_len);+ table_.Insert(buf);+}++bool MemTable::Get(const LookupKey& key, std::string* value, Status* s) {+ Slice memkey = key.memtable_key();+ Table::Iterator iter(&table_);+ iter.Seek(memkey.data());+ if (iter.Valid()) {+ // entry format is:+ // klength varint32+ // userkey char[klength]+ // tag uint64+ // vlength varint32+ // value char[vlength]+ // Check that it belongs to same user key. We do not check the+ // sequence number since the Seek() call above should have skipped+ // all entries with overly large sequence numbers.+ const char* entry = iter.key();+ uint32_t key_length;+ const char* key_ptr = GetVarint32Ptr(entry, entry+5, &key_length);+ if (comparator_.comparator.user_comparator()->Compare(+ Slice(key_ptr, key_length - 8),+ key.user_key()) == 0) {+ // Correct user key+ const uint64_t tag = DecodeFixed64(key_ptr + key_length - 8);+ switch (static_cast<ValueType>(tag & 0xff)) {+ case kTypeValue: {+ Slice v = GetLengthPrefixedSlice(key_ptr + key_length);+ value->assign(v.data(), v.size());+ return true;+ }+ case kTypeDeletion:+ *s = Status::NotFound(Slice());+ return true;+ }+ }+ }+ return false;+}++} // namespace leveldb
+ cbits/leveldb/db/memtable.h view
@@ -0,0 +1,91 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_MEMTABLE_H_+#define STORAGE_LEVELDB_DB_MEMTABLE_H_++#include <string>+#include "leveldb/db.h"+#include "db/dbformat.h"+#include "db/skiplist.h"+#include "util/arena.h"++namespace leveldb {++class InternalKeyComparator;+class Mutex;+class MemTableIterator;++class MemTable {+ public:+ // MemTables are reference counted. The initial reference count+ // is zero and the caller must call Ref() at least once.+ explicit MemTable(const InternalKeyComparator& comparator);++ // Increase reference count.+ void Ref() { ++refs_; }++ // Drop reference count. Delete if no more references exist.+ void Unref() {+ --refs_;+ assert(refs_ >= 0);+ if (refs_ <= 0) {+ delete this;+ }+ }++ // Returns an estimate of the number of bytes of data in use by this+ // data structure.+ //+ // REQUIRES: external synchronization to prevent simultaneous+ // operations on the same MemTable.+ size_t ApproximateMemoryUsage();++ // Return an iterator that yields the contents of the memtable.+ //+ // The caller must ensure that the underlying MemTable remains live+ // while the returned iterator is live. The keys returned by this+ // iterator are internal keys encoded by AppendInternalKey in the+ // db/format.{h,cc} module.+ Iterator* NewIterator();++ // Add an entry into memtable that maps key to value at the+ // specified sequence number and with the specified type.+ // Typically value will be empty if type==kTypeDeletion.+ void Add(SequenceNumber seq, ValueType type,+ const Slice& key,+ const Slice& value);++ // If memtable contains a value for key, store it in *value and return true.+ // If memtable contains a deletion for key, store a NotFound() error+ // in *status and return true.+ // Else, return false.+ bool Get(const LookupKey& key, std::string* value, Status* s);++ private:+ ~MemTable(); // Private since only Unref() should be used to delete it++ struct KeyComparator {+ const InternalKeyComparator comparator;+ explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) { }+ int operator()(const char* a, const char* b) const;+ };+ friend class MemTableIterator;+ friend class MemTableBackwardIterator;++ typedef SkipList<const char*, KeyComparator> Table;++ KeyComparator comparator_;+ int refs_;+ Arena arena_;+ Table table_;++ // No copying allowed+ MemTable(const MemTable&);+ void operator=(const MemTable&);+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_MEMTABLE_H_
+ cbits/leveldb/db/repair.cc view
@@ -0,0 +1,387 @@+// 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.+//+// We recover the contents of the descriptor from the other files we find.+// (1) Any log files are first converted to tables+// (2) We scan every table to compute+// (a) smallest/largest for the table+// (b) largest sequence number in the table+// (3) We generate descriptor contents:+// - log number is set to zero+// - next-file-number is set to 1 + largest file number we found+// - last-sequence-number is set to largest sequence# found across+// all tables (see 2c)+// - compaction pointers are cleared+// - every table file is added at level 0+//+// Possible optimization 1:+// (a) Compute total size and use to pick appropriate max-level M+// (b) Sort tables by largest sequence# in the table+// (c) For each table: if it overlaps earlier table, place in level-0,+// else place in level-M.+// Possible optimization 2:+// Store per-table metadata (smallest, largest, largest-seq#, ...)+// in the table's meta section to speed up ScanTable.++#include "db/builder.h"+#include "db/db_impl.h"+#include "db/dbformat.h"+#include "db/filename.h"+#include "db/log_reader.h"+#include "db/log_writer.h"+#include "db/memtable.h"+#include "db/table_cache.h"+#include "db/version_edit.h"+#include "db/write_batch_internal.h"+#include "leveldb/comparator.h"+#include "leveldb/db.h"+#include "leveldb/env.h"++namespace leveldb {++namespace {++class Repairer {+ public:+ Repairer(const std::string& dbname, const Options& options)+ : dbname_(dbname),+ env_(options.env),+ icmp_(options.comparator),+ options_(SanitizeOptions(dbname, &icmp_, options)),+ owns_info_log_(options_.info_log != options.info_log),+ owns_cache_(options_.block_cache != options.block_cache),+ next_file_number_(1) {+ // TableCache can be small since we expect each table to be opened once.+ table_cache_ = new TableCache(dbname_, &options_, 10);+ }++ ~Repairer() {+ delete table_cache_;+ if (owns_info_log_) {+ delete options_.info_log;+ }+ if (owns_cache_) {+ delete options_.block_cache;+ }+ }++ Status Run() {+ Status status = FindFiles();+ if (status.ok()) {+ ConvertLogFilesToTables();+ ExtractMetaData();+ status = WriteDescriptor();+ }+ if (status.ok()) {+ unsigned long long bytes = 0;+ for (size_t i = 0; i < tables_.size(); i++) {+ bytes += tables_[i].meta.file_size;+ }+ Log(options_.info_log,+ "**** Repaired leveldb %s; "+ "recovered %d files; %llu bytes. "+ "Some data may have been lost. "+ "****",+ dbname_.c_str(),+ static_cast<int>(tables_.size()),+ bytes);+ }+ return status;+ }++ private:+ struct TableInfo {+ FileMetaData meta;+ SequenceNumber max_sequence;+ };++ std::string const dbname_;+ Env* const env_;+ InternalKeyComparator const icmp_;+ Options const options_;+ bool owns_info_log_;+ bool owns_cache_;+ TableCache* table_cache_;+ VersionEdit edit_;++ std::vector<std::string> manifests_;+ std::vector<uint64_t> table_numbers_;+ std::vector<uint64_t> logs_;+ std::vector<TableInfo> tables_;+ uint64_t next_file_number_;++ Status FindFiles() {+ std::vector<std::string> filenames;+ Status status = env_->GetChildren(dbname_, &filenames);+ if (!status.ok()) {+ return status;+ }+ if (filenames.empty()) {+ return Status::IOError(dbname_, "repair found no files");+ }++ uint64_t number;+ FileType type;+ for (size_t i = 0; i < filenames.size(); i++) {+ if (ParseFileName(filenames[i], &number, &type)) {+ if (type == kDescriptorFile) {+ manifests_.push_back(filenames[i]);+ } else {+ if (number + 1 > next_file_number_) {+ next_file_number_ = number + 1;+ }+ if (type == kLogFile) {+ logs_.push_back(number);+ } else if (type == kTableFile) {+ table_numbers_.push_back(number);+ } else {+ // Ignore other files+ }+ }+ }+ }+ return status;+ }++ void ConvertLogFilesToTables() {+ for (size_t i = 0; i < logs_.size(); i++) {+ std::string logname = LogFileName(dbname_, logs_[i]);+ Status status = ConvertLogToTable(logs_[i]);+ if (!status.ok()) {+ Log(options_.info_log, "Log #%llu: ignoring conversion error: %s",+ (unsigned long long) logs_[i],+ status.ToString().c_str());+ }+ ArchiveFile(logname);+ }+ }++ Status ConvertLogToTable(uint64_t log) {+ struct LogReporter : public log::Reader::Reporter {+ Env* env;+ Logger* info_log;+ uint64_t lognum;+ virtual void Corruption(size_t bytes, const Status& s) {+ // We print error messages for corruption, but continue repairing.+ Log(info_log, "Log #%llu: dropping %d bytes; %s",+ (unsigned long long) lognum,+ static_cast<int>(bytes),+ s.ToString().c_str());+ }+ };++ // Open the log file+ std::string logname = LogFileName(dbname_, log);+ SequentialFile* lfile;+ Status status = env_->NewSequentialFile(logname, &lfile);+ if (!status.ok()) {+ return status;+ }++ // Create the log reader.+ LogReporter reporter;+ reporter.env = env_;+ reporter.info_log = options_.info_log;+ reporter.lognum = log;+ // We intentially make log::Reader do checksumming so that+ // corruptions cause entire commits to be skipped instead of+ // propagating bad information (like overly large sequence+ // numbers).+ log::Reader reader(lfile, &reporter, false/*do not checksum*/,+ 0/*initial_offset*/);++ // Read all the records and add to a memtable+ std::string scratch;+ Slice record;+ WriteBatch batch;+ MemTable* mem = new MemTable(icmp_);+ mem->Ref();+ int counter = 0;+ while (reader.ReadRecord(&record, &scratch)) {+ if (record.size() < 12) {+ reporter.Corruption(+ record.size(), Status::Corruption("log record too small"));+ continue;+ }+ WriteBatchInternal::SetContents(&batch, record);+ status = WriteBatchInternal::InsertInto(&batch, mem);+ if (status.ok()) {+ counter += WriteBatchInternal::Count(&batch);+ } else {+ Log(options_.info_log, "Log #%llu: ignoring %s",+ (unsigned long long) log,+ status.ToString().c_str());+ status = Status::OK(); // Keep going with rest of file+ }+ }+ delete lfile;++ // Do not record a version edit for this conversion to a Table+ // since ExtractMetaData() will also generate edits.+ FileMetaData meta;+ meta.number = next_file_number_++;+ Iterator* iter = mem->NewIterator();+ status = BuildTable(dbname_, env_, options_, table_cache_, iter, &meta);+ delete iter;+ mem->Unref();+ mem = NULL;+ if (status.ok()) {+ if (meta.file_size > 0) {+ table_numbers_.push_back(meta.number);+ }+ }+ Log(options_.info_log, "Log #%llu: %d ops saved to Table #%llu %s",+ (unsigned long long) log,+ counter,+ (unsigned long long) meta.number,+ status.ToString().c_str());+ return status;+ }++ void ExtractMetaData() {+ std::vector<TableInfo> kept;+ for (size_t i = 0; i < table_numbers_.size(); i++) {+ TableInfo t;+ t.meta.number = table_numbers_[i];+ Status status = ScanTable(&t);+ if (!status.ok()) {+ std::string fname = TableFileName(dbname_, table_numbers_[i]);+ Log(options_.info_log, "Table #%llu: ignoring %s",+ (unsigned long long) table_numbers_[i],+ status.ToString().c_str());+ ArchiveFile(fname);+ } else {+ tables_.push_back(t);+ }+ }+ }++ Status ScanTable(TableInfo* t) {+ std::string fname = TableFileName(dbname_, t->meta.number);+ int counter = 0;+ Status status = env_->GetFileSize(fname, &t->meta.file_size);+ if (status.ok()) {+ Iterator* iter = table_cache_->NewIterator(+ ReadOptions(), t->meta.number, t->meta.file_size);+ bool empty = true;+ ParsedInternalKey parsed;+ t->max_sequence = 0;+ for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {+ Slice key = iter->key();+ if (!ParseInternalKey(key, &parsed)) {+ Log(options_.info_log, "Table #%llu: unparsable key %s",+ (unsigned long long) t->meta.number,+ EscapeString(key).c_str());+ continue;+ }++ counter++;+ if (empty) {+ empty = false;+ t->meta.smallest.DecodeFrom(key);+ }+ t->meta.largest.DecodeFrom(key);+ if (parsed.sequence > t->max_sequence) {+ t->max_sequence = parsed.sequence;+ }+ }+ if (!iter->status().ok()) {+ status = iter->status();+ }+ delete iter;+ }+ Log(options_.info_log, "Table #%llu: %d entries %s",+ (unsigned long long) t->meta.number,+ counter,+ status.ToString().c_str());+ return status;+ }++ Status WriteDescriptor() {+ std::string tmp = TempFileName(dbname_, 1);+ WritableFile* file;+ Status status = env_->NewWritableFile(tmp, &file);+ if (!status.ok()) {+ return status;+ }++ SequenceNumber max_sequence = 0;+ for (size_t i = 0; i < tables_.size(); i++) {+ if (max_sequence < tables_[i].max_sequence) {+ max_sequence = tables_[i].max_sequence;+ }+ }++ edit_.SetComparatorName(icmp_.user_comparator()->Name());+ edit_.SetLogNumber(0);+ edit_.SetNextFile(next_file_number_);+ edit_.SetLastSequence(max_sequence);++ for (size_t i = 0; i < tables_.size(); i++) {+ // TODO(opt): separate out into multiple levels+ const TableInfo& t = tables_[i];+ edit_.AddFile(0, t.meta.number, t.meta.file_size,+ t.meta.smallest, t.meta.largest);+ }++ //fprintf(stderr, "NewDescriptor:\n%s\n", edit_.DebugString().c_str());+ {+ log::Writer log(file);+ std::string record;+ edit_.EncodeTo(&record);+ status = log.AddRecord(record);+ }+ if (status.ok()) {+ status = file->Close();+ }+ delete file;+ file = NULL;++ if (!status.ok()) {+ env_->DeleteFile(tmp);+ } else {+ // Discard older manifests+ for (size_t i = 0; i < manifests_.size(); i++) {+ ArchiveFile(dbname_ + "/" + manifests_[i]);+ }++ // Install new manifest+ status = env_->RenameFile(tmp, DescriptorFileName(dbname_, 1));+ if (status.ok()) {+ status = SetCurrentFile(env_, dbname_, 1);+ } else {+ env_->DeleteFile(tmp);+ }+ }+ return status;+ }++ void ArchiveFile(const std::string& fname) {+ // Move into another directory. E.g., for+ // dir/foo+ // rename to+ // dir/lost/foo+ const char* slash = strrchr(fname.c_str(), '/');+ std::string new_dir;+ if (slash != NULL) {+ new_dir.assign(fname.data(), slash - fname.data());+ }+ new_dir.append("/lost");+ env_->CreateDir(new_dir); // Ignore error+ std::string new_file = new_dir;+ new_file.append("/");+ new_file.append((slash == NULL) ? fname.c_str() : slash + 1);+ Status s = env_->RenameFile(fname, new_file);+ Log(options_.info_log, "Archiving %s: %s\n",+ fname.c_str(), s.ToString().c_str());+ }+};+} // namespace++Status RepairDB(const std::string& dbname, const Options& options) {+ Repairer repairer(dbname, options);+ return repairer.Run();+}++} // namespace leveldb
+ cbits/leveldb/db/skiplist.h view
@@ -0,0 +1,378 @@+// 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.+//+// Thread safety+// -------------+//+// Writes require external synchronization, most likely a mutex.+// Reads require a guarantee that the SkipList will not be destroyed+// while the read is in progress. Apart from that, reads progress+// without any internal locking or synchronization.+//+// Invariants:+//+// (1) Allocated nodes are never deleted until the SkipList is+// destroyed. This is trivially guaranteed by the code since we+// never delete any skip list nodes.+//+// (2) The contents of a Node except for the next/prev pointers are+// immutable after the Node has been linked into the SkipList.+// Only Insert() modifies the list, and it is careful to initialize+// a node and use release-stores to publish the nodes in one or+// more lists.+//+// ... prev vs. next pointer ordering ...++#include <assert.h>+#include <stdlib.h>+#include "port/port.h"+#include "util/arena.h"+#include "util/random.h"++namespace leveldb {++class Arena;++template<typename Key, class Comparator>+class SkipList {+ private:+ struct Node;++ public:+ // Create a new SkipList object that will use "cmp" for comparing keys,+ // and will allocate memory using "*arena". Objects allocated in the arena+ // must remain allocated for the lifetime of the skiplist object.+ explicit SkipList(Comparator cmp, Arena* arena);++ // Insert key into the list.+ // REQUIRES: nothing that compares equal to key is currently in the list.+ void Insert(const Key& key);++ // Returns true iff an entry that compares equal to key is in the list.+ bool Contains(const Key& key) const;++ // Iteration over the contents of a skip list+ class Iterator {+ public:+ // Initialize an iterator over the specified list.+ // The returned iterator is not valid.+ explicit Iterator(const SkipList* list);++ // Returns true iff the iterator is positioned at a valid node.+ bool Valid() const;++ // Returns the key at the current position.+ // REQUIRES: Valid()+ const Key& key() const;++ // Advances to the next position.+ // REQUIRES: Valid()+ void Next();++ // Advances to the previous position.+ // REQUIRES: Valid()+ void Prev();++ // Advance to the first entry with a key >= target+ void Seek(const Key& target);++ // Position at the first entry in list.+ // Final state of iterator is Valid() iff list is not empty.+ void SeekToFirst();++ // Position at the last entry in list.+ // Final state of iterator is Valid() iff list is not empty.+ void SeekToLast();++ private:+ const SkipList* list_;+ Node* node_;+ // Intentionally copyable+ };++ private:+ enum { kMaxHeight = 12 };++ // Immutable after construction+ Comparator const compare_;+ Arena* const arena_; // Arena used for allocations of nodes++ Node* const head_;++ // Modified only by Insert(). Read racily by readers, but stale+ // values are ok.+ port::AtomicPointer max_height_; // Height of the entire list++ inline int GetMaxHeight() const {+ return reinterpret_cast<intptr_t>(max_height_.NoBarrier_Load());+ }++ // Read/written only by Insert().+ Random rnd_;++ Node* NewNode(const Key& key, int height);+ int RandomHeight();+ bool Equal(const Key& a, const Key& b) const { return (compare_(a, b) == 0); }++ // Return true if key is greater than the data stored in "n"+ bool KeyIsAfterNode(const Key& key, Node* n) const;++ // Return the earliest node that comes at or after key.+ // Return NULL if there is no such node.+ //+ // If prev is non-NULL, fills prev[level] with pointer to previous+ // node at "level" for every level in [0..max_height_-1].+ Node* FindGreaterOrEqual(const Key& key, Node** prev) const;++ // Return the latest node with a key < key.+ // Return head_ if there is no such node.+ Node* FindLessThan(const Key& key) const;++ // Return the last node in the list.+ // Return head_ if list is empty.+ Node* FindLast() const;++ // No copying allowed+ SkipList(const SkipList&);+ void operator=(const SkipList&);+};++// Implementation details follow+template<typename Key, class Comparator>+struct SkipList<Key,Comparator>::Node {+ explicit Node(const Key& k) : key(k) { }++ Key const key;++ // Accessors/mutators for links. Wrapped in methods so we can+ // add the appropriate barriers as necessary.+ Node* Next(int n) {+ assert(n >= 0);+ // Use an 'acquire load' so that we observe a fully initialized+ // version of the returned Node.+ return reinterpret_cast<Node*>(next_[n].Acquire_Load());+ }+ void SetNext(int n, Node* x) {+ assert(n >= 0);+ // Use a 'release store' so that anybody who reads through this+ // pointer observes a fully initialized version of the inserted node.+ next_[n].Release_Store(x);+ }++ // No-barrier variants that can be safely used in a few locations.+ Node* NoBarrier_Next(int n) {+ assert(n >= 0);+ return reinterpret_cast<Node*>(next_[n].NoBarrier_Load());+ }+ void NoBarrier_SetNext(int n, Node* x) {+ assert(n >= 0);+ next_[n].NoBarrier_Store(x);+ }++ private:+ // Array of length equal to the node height. next_[0] is lowest level link.+ port::AtomicPointer next_[1];+};++template<typename Key, class Comparator>+typename SkipList<Key,Comparator>::Node*+SkipList<Key,Comparator>::NewNode(const Key& key, int height) {+ char* mem = arena_->AllocateAligned(+ sizeof(Node) + sizeof(port::AtomicPointer) * (height - 1));+ return new (mem) Node(key);+}++template<typename Key, class Comparator>+inline SkipList<Key,Comparator>::Iterator::Iterator(const SkipList* list) {+ list_ = list;+ node_ = NULL;+}++template<typename Key, class Comparator>+inline bool SkipList<Key,Comparator>::Iterator::Valid() const {+ return node_ != NULL;+}++template<typename Key, class Comparator>+inline const Key& SkipList<Key,Comparator>::Iterator::key() const {+ assert(Valid());+ return node_->key;+}++template<typename Key, class Comparator>+inline void SkipList<Key,Comparator>::Iterator::Next() {+ assert(Valid());+ node_ = node_->Next(0);+}++template<typename Key, class Comparator>+inline void SkipList<Key,Comparator>::Iterator::Prev() {+ // Instead of using explicit "prev" links, we just search for the+ // last node that falls before key.+ assert(Valid());+ node_ = list_->FindLessThan(node_->key);+ if (node_ == list_->head_) {+ node_ = NULL;+ }+}++template<typename Key, class Comparator>+inline void SkipList<Key,Comparator>::Iterator::Seek(const Key& target) {+ node_ = list_->FindGreaterOrEqual(target, NULL);+}++template<typename Key, class Comparator>+inline void SkipList<Key,Comparator>::Iterator::SeekToFirst() {+ node_ = list_->head_->Next(0);+}++template<typename Key, class Comparator>+inline void SkipList<Key,Comparator>::Iterator::SeekToLast() {+ node_ = list_->FindLast();+ if (node_ == list_->head_) {+ node_ = NULL;+ }+}++template<typename Key, class Comparator>+int SkipList<Key,Comparator>::RandomHeight() {+ // Increase height with probability 1 in kBranching+ static const unsigned int kBranching = 4;+ int height = 1;+ while (height < kMaxHeight && ((rnd_.Next() % kBranching) == 0)) {+ height++;+ }+ assert(height > 0);+ assert(height <= kMaxHeight);+ return height;+}++template<typename Key, class Comparator>+bool SkipList<Key,Comparator>::KeyIsAfterNode(const Key& key, Node* n) const {+ // NULL n is considered infinite+ return (n != NULL) && (compare_(n->key, key) < 0);+}++template<typename Key, class Comparator>+typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindGreaterOrEqual(const Key& key, Node** prev)+ const {+ Node* x = head_;+ int level = GetMaxHeight() - 1;+ while (true) {+ Node* next = x->Next(level);+ if (KeyIsAfterNode(key, next)) {+ // Keep searching in this list+ x = next;+ } else {+ if (prev != NULL) prev[level] = x;+ if (level == 0) {+ return next;+ } else {+ // Switch to next list+ level--;+ }+ }+ }+}++template<typename Key, class Comparator>+typename SkipList<Key,Comparator>::Node*+SkipList<Key,Comparator>::FindLessThan(const Key& key) const {+ Node* x = head_;+ int level = GetMaxHeight() - 1;+ while (true) {+ assert(x == head_ || compare_(x->key, key) < 0);+ Node* next = x->Next(level);+ if (next == NULL || compare_(next->key, key) >= 0) {+ if (level == 0) {+ return x;+ } else {+ // Switch to next list+ level--;+ }+ } else {+ x = next;+ }+ }+}++template<typename Key, class Comparator>+typename SkipList<Key,Comparator>::Node* SkipList<Key,Comparator>::FindLast()+ const {+ Node* x = head_;+ int level = GetMaxHeight() - 1;+ while (true) {+ Node* next = x->Next(level);+ if (next == NULL) {+ if (level == 0) {+ return x;+ } else {+ // Switch to next list+ level--;+ }+ } else {+ x = next;+ }+ }+}++template<typename Key, class Comparator>+SkipList<Key,Comparator>::SkipList(Comparator cmp, Arena* arena)+ : compare_(cmp),+ arena_(arena),+ head_(NewNode(0 /* any key will do */, kMaxHeight)),+ max_height_(reinterpret_cast<void*>(1)),+ rnd_(0xdeadbeef) {+ for (int i = 0; i < kMaxHeight; i++) {+ head_->SetNext(i, NULL);+ }+}++template<typename Key, class Comparator>+void SkipList<Key,Comparator>::Insert(const Key& key) {+ // TODO(opt): We can use a barrier-free variant of FindGreaterOrEqual()+ // here since Insert() is externally synchronized.+ Node* prev[kMaxHeight];+ Node* x = FindGreaterOrEqual(key, prev);++ // Our data structure does not allow duplicate insertion+ assert(x == NULL || !Equal(key, x->key));++ int height = RandomHeight();+ if (height > GetMaxHeight()) {+ for (int i = GetMaxHeight(); i < height; i++) {+ prev[i] = head_;+ }+ //fprintf(stderr, "Change height from %d to %d\n", max_height_, height);++ // It is ok to mutate max_height_ without any synchronization+ // with concurrent readers. A concurrent reader that observes+ // the new value of max_height_ will see either the old value of+ // new level pointers from head_ (NULL), or a new value set in+ // the loop below. In the former case the reader will+ // immediately drop to the next level since NULL sorts after all+ // keys. In the latter case the reader will use the new node.+ max_height_.NoBarrier_Store(reinterpret_cast<void*>(height));+ }++ x = NewNode(key, height);+ for (int i = 0; i < height; i++) {+ // NoBarrier_SetNext() suffices since we will add a barrier when+ // we publish a pointer to "x" in prev[i].+ x->NoBarrier_SetNext(i, prev[i]->NoBarrier_Next(i));+ prev[i]->SetNext(i, x);+ }+}++template<typename Key, class Comparator>+bool SkipList<Key,Comparator>::Contains(const Key& key) const {+ Node* x = FindGreaterOrEqual(key, NULL);+ if (x != NULL && Equal(key, x->key)) {+ return true;+ } else {+ return false;+ }+}++} // namespace leveldb
+ cbits/leveldb/db/skiplist_test.cc view
@@ -0,0 +1,378 @@+// 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 "db/skiplist.h"+#include <set>+#include "leveldb/env.h"+#include "util/arena.h"+#include "util/hash.h"+#include "util/random.h"+#include "util/testharness.h"++namespace leveldb {++typedef uint64_t Key;++struct Comparator {+ int operator()(const Key& a, const Key& b) const {+ if (a < b) {+ return -1;+ } else if (a > b) {+ return +1;+ } else {+ return 0;+ }+ }+};++class SkipTest { };++TEST(SkipTest, Empty) {+ Arena arena;+ Comparator cmp;+ SkipList<Key, Comparator> list(cmp, &arena);+ ASSERT_TRUE(!list.Contains(10));++ SkipList<Key, Comparator>::Iterator iter(&list);+ ASSERT_TRUE(!iter.Valid());+ iter.SeekToFirst();+ ASSERT_TRUE(!iter.Valid());+ iter.Seek(100);+ ASSERT_TRUE(!iter.Valid());+ iter.SeekToLast();+ ASSERT_TRUE(!iter.Valid());+}++TEST(SkipTest, InsertAndLookup) {+ const int N = 2000;+ const int R = 5000;+ Random rnd(1000);+ std::set<Key> keys;+ Arena arena;+ Comparator cmp;+ SkipList<Key, Comparator> list(cmp, &arena);+ for (int i = 0; i < N; i++) {+ Key key = rnd.Next() % R;+ if (keys.insert(key).second) {+ list.Insert(key);+ }+ }++ for (int i = 0; i < R; i++) {+ if (list.Contains(i)) {+ ASSERT_EQ(keys.count(i), 1);+ } else {+ ASSERT_EQ(keys.count(i), 0);+ }+ }++ // Simple iterator tests+ {+ SkipList<Key, Comparator>::Iterator iter(&list);+ ASSERT_TRUE(!iter.Valid());++ iter.Seek(0);+ ASSERT_TRUE(iter.Valid());+ ASSERT_EQ(*(keys.begin()), iter.key());++ iter.SeekToFirst();+ ASSERT_TRUE(iter.Valid());+ ASSERT_EQ(*(keys.begin()), iter.key());++ iter.SeekToLast();+ ASSERT_TRUE(iter.Valid());+ ASSERT_EQ(*(keys.rbegin()), iter.key());+ }++ // Forward iteration test+ for (int i = 0; i < R; i++) {+ SkipList<Key, Comparator>::Iterator iter(&list);+ iter.Seek(i);++ // Compare against model iterator+ std::set<Key>::iterator model_iter = keys.lower_bound(i);+ for (int j = 0; j < 3; j++) {+ if (model_iter == keys.end()) {+ ASSERT_TRUE(!iter.Valid());+ break;+ } else {+ ASSERT_TRUE(iter.Valid());+ ASSERT_EQ(*model_iter, iter.key());+ ++model_iter;+ iter.Next();+ }+ }+ }++ // Backward iteration test+ {+ SkipList<Key, Comparator>::Iterator iter(&list);+ iter.SeekToLast();++ // Compare against model iterator+ for (std::set<Key>::reverse_iterator model_iter = keys.rbegin();+ model_iter != keys.rend();+ ++model_iter) {+ ASSERT_TRUE(iter.Valid());+ ASSERT_EQ(*model_iter, iter.key());+ iter.Prev();+ }+ ASSERT_TRUE(!iter.Valid());+ }+}++// We want to make sure that with a single writer and multiple+// concurrent readers (with no synchronization other than when a+// reader's iterator is created), the reader always observes all the+// data that was present in the skip list when the iterator was+// constructor. Because insertions are happening concurrently, we may+// also observe new values that were inserted since the iterator was+// constructed, but we should never miss any values that were present+// at iterator construction time.+//+// We generate multi-part keys:+// <key,gen,hash>+// where:+// key is in range [0..K-1]+// gen is a generation number for key+// hash is hash(key,gen)+//+// The insertion code picks a random key, sets gen to be 1 + the last+// generation number inserted for that key, and sets hash to Hash(key,gen).+//+// At the beginning of a read, we snapshot the last inserted+// generation number for each key. We then iterate, including random+// calls to Next() and Seek(). For every key we encounter, we+// check that it is either expected given the initial snapshot or has+// been concurrently added since the iterator started.+class ConcurrentTest {+ private:+ static const uint32_t K = 4;++ static uint64_t key(Key key) { return (key >> 40); }+ static uint64_t gen(Key key) { return (key >> 8) & 0xffffffffu; }+ static uint64_t hash(Key key) { return key & 0xff; }++ static uint64_t HashNumbers(uint64_t k, uint64_t g) {+ uint64_t data[2] = { k, g };+ return Hash(reinterpret_cast<char*>(data), sizeof(data), 0);+ }++ static Key MakeKey(uint64_t k, uint64_t g) {+ assert(sizeof(Key) == sizeof(uint64_t));+ assert(k <= K); // We sometimes pass K to seek to the end of the skiplist+ assert(g <= 0xffffffffu);+ return ((k << 40) | (g << 8) | (HashNumbers(k, g) & 0xff));+ }++ static bool IsValidKey(Key k) {+ return hash(k) == (HashNumbers(key(k), gen(k)) & 0xff);+ }++ static Key RandomTarget(Random* rnd) {+ switch (rnd->Next() % 10) {+ case 0:+ // Seek to beginning+ return MakeKey(0, 0);+ case 1:+ // Seek to end+ return MakeKey(K, 0);+ default:+ // Seek to middle+ return MakeKey(rnd->Next() % K, 0);+ }+ }++ // Per-key generation+ struct State {+ port::AtomicPointer generation[K];+ void Set(int k, intptr_t v) {+ generation[k].Release_Store(reinterpret_cast<void*>(v));+ }+ intptr_t Get(int k) {+ return reinterpret_cast<intptr_t>(generation[k].Acquire_Load());+ }++ State() {+ for (int k = 0; k < K; k++) {+ Set(k, 0);+ }+ }+ };++ // Current state of the test+ State current_;++ Arena arena_;++ // SkipList is not protected by mu_. We just use a single writer+ // thread to modify it.+ SkipList<Key, Comparator> list_;++ public:+ ConcurrentTest() : list_(Comparator(), &arena_) { }++ // REQUIRES: External synchronization+ void WriteStep(Random* rnd) {+ const uint32_t k = rnd->Next() % K;+ const intptr_t g = current_.Get(k) + 1;+ const Key key = MakeKey(k, g);+ list_.Insert(key);+ current_.Set(k, g);+ }++ void ReadStep(Random* rnd) {+ // Remember the initial committed state of the skiplist.+ State initial_state;+ for (int k = 0; k < K; k++) {+ initial_state.Set(k, current_.Get(k));+ }++ Key pos = RandomTarget(rnd);+ SkipList<Key, Comparator>::Iterator iter(&list_);+ iter.Seek(pos);+ while (true) {+ Key current;+ if (!iter.Valid()) {+ current = MakeKey(K, 0);+ } else {+ current = iter.key();+ ASSERT_TRUE(IsValidKey(current)) << current;+ }+ ASSERT_LE(pos, current) << "should not go backwards";++ // Verify that everything in [pos,current) was not present in+ // initial_state.+ while (pos < current) {+ ASSERT_LT(key(pos), K) << pos;++ // Note that generation 0 is never inserted, so it is ok if+ // <*,0,*> is missing.+ ASSERT_TRUE((gen(pos) == 0) ||+ (gen(pos) > initial_state.Get(key(pos)))+ ) << "key: " << key(pos)+ << "; gen: " << gen(pos)+ << "; initgen: "+ << initial_state.Get(key(pos));++ // Advance to next key in the valid key space+ if (key(pos) < key(current)) {+ pos = MakeKey(key(pos) + 1, 0);+ } else {+ pos = MakeKey(key(pos), gen(pos) + 1);+ }+ }++ if (!iter.Valid()) {+ break;+ }++ if (rnd->Next() % 2) {+ iter.Next();+ pos = MakeKey(key(pos), gen(pos) + 1);+ } else {+ Key new_target = RandomTarget(rnd);+ if (new_target > pos) {+ pos = new_target;+ iter.Seek(new_target);+ }+ }+ }+ }+};+const uint32_t ConcurrentTest::K;++// Simple test that does single-threaded testing of the ConcurrentTest+// scaffolding.+TEST(SkipTest, ConcurrentWithoutThreads) {+ ConcurrentTest test;+ Random rnd(test::RandomSeed());+ for (int i = 0; i < 10000; i++) {+ test.ReadStep(&rnd);+ test.WriteStep(&rnd);+ }+}++class TestState {+ public:+ ConcurrentTest t_;+ int seed_;+ port::AtomicPointer quit_flag_;++ enum ReaderState {+ STARTING,+ RUNNING,+ DONE+ };++ explicit TestState(int s)+ : seed_(s),+ quit_flag_(NULL),+ state_(STARTING),+ state_cv_(&mu_) {}++ void Wait(ReaderState s) {+ mu_.Lock();+ while (state_ != s) {+ state_cv_.Wait();+ }+ mu_.Unlock();+ }++ void Change(ReaderState s) {+ mu_.Lock();+ state_ = s;+ state_cv_.Signal();+ mu_.Unlock();+ }++ private:+ port::Mutex mu_;+ ReaderState state_;+ port::CondVar state_cv_;+};++static void ConcurrentReader(void* arg) {+ TestState* state = reinterpret_cast<TestState*>(arg);+ Random rnd(state->seed_);+ int64_t reads = 0;+ state->Change(TestState::RUNNING);+ while (!state->quit_flag_.Acquire_Load()) {+ state->t_.ReadStep(&rnd);+ ++reads;+ }+ state->Change(TestState::DONE);+}++static void RunConcurrent(int run) {+ const int seed = test::RandomSeed() + (run * 100);+ Random rnd(seed);+ const int N = 1000;+ const int kSize = 1000;+ for (int i = 0; i < N; i++) {+ if ((i % 100) == 0) {+ fprintf(stderr, "Run %d of %d\n", i, N);+ }+ TestState state(seed + 1);+ Env::Default()->Schedule(ConcurrentReader, &state);+ state.Wait(TestState::RUNNING);+ for (int i = 0; i < kSize; i++) {+ state.t_.WriteStep(&rnd);+ }+ state.quit_flag_.Release_Store(&state); // Any non-NULL arg will do+ state.Wait(TestState::DONE);+ }+}++TEST(SkipTest, Concurrent1) { RunConcurrent(1); }+TEST(SkipTest, Concurrent2) { RunConcurrent(2); }+TEST(SkipTest, Concurrent3) { RunConcurrent(3); }+TEST(SkipTest, Concurrent4) { RunConcurrent(4); }+TEST(SkipTest, Concurrent5) { RunConcurrent(5); }++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/db/snapshot.h view
@@ -0,0 +1,66 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_+#define STORAGE_LEVELDB_DB_SNAPSHOT_H_++#include "leveldb/db.h"++namespace leveldb {++class SnapshotList;++// Snapshots are kept in a doubly-linked list in the DB.+// Each SnapshotImpl corresponds to a particular sequence number.+class SnapshotImpl : public Snapshot {+ public:+ SequenceNumber number_; // const after creation++ private:+ friend class SnapshotList;++ // SnapshotImpl is kept in a doubly-linked circular list+ SnapshotImpl* prev_;+ SnapshotImpl* next_;++ SnapshotList* list_; // just for sanity checks+};++class SnapshotList {+ public:+ SnapshotList() {+ list_.prev_ = &list_;+ list_.next_ = &list_;+ }++ bool empty() const { return list_.next_ == &list_; }+ SnapshotImpl* oldest() const { assert(!empty()); return list_.next_; }+ SnapshotImpl* newest() const { assert(!empty()); return list_.prev_; }++ const SnapshotImpl* New(SequenceNumber seq) {+ SnapshotImpl* s = new SnapshotImpl;+ s->number_ = seq;+ s->list_ = this;+ s->next_ = &list_;+ s->prev_ = list_.prev_;+ s->prev_->next_ = s;+ s->next_->prev_ = s;+ return s;+ }++ void Delete(const SnapshotImpl* s) {+ assert(s->list_ == this);+ s->prev_->next_ = s->next_;+ s->next_->prev_ = s->prev_;+ delete s;+ }++ private:+ // Dummy head of doubly-linked list of snapshots+ SnapshotImpl list_;+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_SNAPSHOT_H_
+ cbits/leveldb/db/table_cache.cc view
@@ -0,0 +1,95 @@+// 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 "db/table_cache.h"++#include "db/filename.h"+#include "leveldb/env.h"+#include "leveldb/table.h"+#include "util/coding.h"++namespace leveldb {++struct TableAndFile {+ RandomAccessFile* file;+ Table* table;+};++static void DeleteEntry(const Slice& key, void* value) {+ TableAndFile* tf = reinterpret_cast<TableAndFile*>(value);+ delete tf->table;+ delete tf->file;+ delete tf;+}++static void UnrefEntry(void* arg1, void* arg2) {+ Cache* cache = reinterpret_cast<Cache*>(arg1);+ Cache::Handle* h = reinterpret_cast<Cache::Handle*>(arg2);+ cache->Release(h);+}++TableCache::TableCache(const std::string& dbname,+ const Options* options,+ int entries)+ : env_(options->env),+ dbname_(dbname),+ options_(options),+ cache_(NewLRUCache(entries)) {+}++TableCache::~TableCache() {+ delete cache_;+}++Iterator* TableCache::NewIterator(const ReadOptions& options,+ uint64_t file_number,+ uint64_t file_size,+ Table** tableptr) {+ if (tableptr != NULL) {+ *tableptr = NULL;+ }++ char buf[sizeof(file_number)];+ EncodeFixed64(buf, file_number);+ Slice key(buf, sizeof(buf));+ Cache::Handle* handle = cache_->Lookup(key);+ if (handle == NULL) {+ std::string fname = TableFileName(dbname_, file_number);+ RandomAccessFile* file = NULL;+ Table* table = NULL;+ Status s = env_->NewRandomAccessFile(fname, &file);+ if (s.ok()) {+ s = Table::Open(*options_, file, file_size, &table);+ }++ if (!s.ok()) {+ assert(table == NULL);+ delete file;+ // We do not cache error results so that if the error is transient,+ // or somebody repairs the file, we recover automatically.+ return NewErrorIterator(s);+ }++ TableAndFile* tf = new TableAndFile;+ tf->file = file;+ tf->table = table;+ handle = cache_->Insert(key, tf, 1, &DeleteEntry);+ }++ Table* table = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;+ Iterator* result = table->NewIterator(options);+ result->RegisterCleanup(&UnrefEntry, cache_, handle);+ if (tableptr != NULL) {+ *tableptr = table;+ }+ return result;+}++void TableCache::Evict(uint64_t file_number) {+ char buf[sizeof(file_number)];+ EncodeFixed64(buf, file_number);+ cache_->Erase(Slice(buf, sizeof(buf)));+}++} // namespace leveldb
+ cbits/leveldb/db/table_cache.h view
@@ -0,0 +1,50 @@+// 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.+//+// Thread-safe (provides internal synchronization)++#ifndef STORAGE_LEVELDB_DB_TABLE_CACHE_H_+#define STORAGE_LEVELDB_DB_TABLE_CACHE_H_++#include <string>+#include <stdint.h>+#include "db/dbformat.h"+#include "leveldb/cache.h"+#include "leveldb/table.h"+#include "port/port.h"++namespace leveldb {++class Env;++class TableCache {+ public:+ TableCache(const std::string& dbname, const Options* options, int entries);+ ~TableCache();++ // Return an iterator for the specified file number (the corresponding+ // file length must be exactly "file_size" bytes). If "tableptr" is+ // non-NULL, also sets "*tableptr" to point to the Table object+ // underlying the returned iterator, or NULL if no Table object underlies+ // the returned iterator. The returned "*tableptr" object is owned by+ // the cache and should not be deleted, and is valid for as long as the+ // returned iterator is live.+ Iterator* NewIterator(const ReadOptions& options,+ uint64_t file_number,+ uint64_t file_size,+ Table** tableptr = NULL);++ // Evict any entry for the specified file number+ void Evict(uint64_t file_number);++ private:+ Env* const env_;+ const std::string dbname_;+ const Options* options_;+ Cache* cache_;+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_TABLE_CACHE_H_
+ cbits/leveldb/db/version_edit.cc view
@@ -0,0 +1,266 @@+// 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 "db/version_edit.h"++#include "db/version_set.h"+#include "util/coding.h"++namespace leveldb {++// Tag numbers for serialized VersionEdit. These numbers are written to+// disk and should not be changed.+enum Tag {+ kComparator = 1,+ kLogNumber = 2,+ kNextFileNumber = 3,+ kLastSequence = 4,+ kCompactPointer = 5,+ kDeletedFile = 6,+ kNewFile = 7,+ // 8 was used for large value refs+ kPrevLogNumber = 9+};++void VersionEdit::Clear() {+ comparator_.clear();+ log_number_ = 0;+ prev_log_number_ = 0;+ last_sequence_ = 0;+ next_file_number_ = 0;+ has_comparator_ = false;+ has_log_number_ = false;+ has_prev_log_number_ = false;+ has_next_file_number_ = false;+ has_last_sequence_ = false;+ deleted_files_.clear();+ new_files_.clear();+}++void VersionEdit::EncodeTo(std::string* dst) const {+ if (has_comparator_) {+ PutVarint32(dst, kComparator);+ PutLengthPrefixedSlice(dst, comparator_);+ }+ if (has_log_number_) {+ PutVarint32(dst, kLogNumber);+ PutVarint64(dst, log_number_);+ }+ if (has_prev_log_number_) {+ PutVarint32(dst, kPrevLogNumber);+ PutVarint64(dst, prev_log_number_);+ }+ if (has_next_file_number_) {+ PutVarint32(dst, kNextFileNumber);+ PutVarint64(dst, next_file_number_);+ }+ if (has_last_sequence_) {+ PutVarint32(dst, kLastSequence);+ PutVarint64(dst, last_sequence_);+ }++ for (size_t i = 0; i < compact_pointers_.size(); i++) {+ PutVarint32(dst, kCompactPointer);+ PutVarint32(dst, compact_pointers_[i].first); // level+ PutLengthPrefixedSlice(dst, compact_pointers_[i].second.Encode());+ }++ for (DeletedFileSet::const_iterator iter = deleted_files_.begin();+ iter != deleted_files_.end();+ ++iter) {+ PutVarint32(dst, kDeletedFile);+ PutVarint32(dst, iter->first); // level+ PutVarint64(dst, iter->second); // file number+ }++ for (size_t i = 0; i < new_files_.size(); i++) {+ const FileMetaData& f = new_files_[i].second;+ PutVarint32(dst, kNewFile);+ PutVarint32(dst, new_files_[i].first); // level+ PutVarint64(dst, f.number);+ PutVarint64(dst, f.file_size);+ PutLengthPrefixedSlice(dst, f.smallest.Encode());+ PutLengthPrefixedSlice(dst, f.largest.Encode());+ }+}++static bool GetInternalKey(Slice* input, InternalKey* dst) {+ Slice str;+ if (GetLengthPrefixedSlice(input, &str)) {+ dst->DecodeFrom(str);+ return true;+ } else {+ return false;+ }+}++static bool GetLevel(Slice* input, int* level) {+ uint32_t v;+ if (GetVarint32(input, &v) &&+ v < config::kNumLevels) {+ *level = v;+ return true;+ } else {+ return false;+ }+}++Status VersionEdit::DecodeFrom(const Slice& src) {+ Clear();+ Slice input = src;+ const char* msg = NULL;+ uint32_t tag;++ // Temporary storage for parsing+ int level;+ uint64_t number;+ FileMetaData f;+ Slice str;+ InternalKey key;++ while (msg == NULL && GetVarint32(&input, &tag)) {+ switch (tag) {+ case kComparator:+ if (GetLengthPrefixedSlice(&input, &str)) {+ comparator_ = str.ToString();+ has_comparator_ = true;+ } else {+ msg = "comparator name";+ }+ break;++ case kLogNumber:+ if (GetVarint64(&input, &log_number_)) {+ has_log_number_ = true;+ } else {+ msg = "log number";+ }+ break;++ case kPrevLogNumber:+ if (GetVarint64(&input, &prev_log_number_)) {+ has_prev_log_number_ = true;+ } else {+ msg = "previous log number";+ }+ break;++ case kNextFileNumber:+ if (GetVarint64(&input, &next_file_number_)) {+ has_next_file_number_ = true;+ } else {+ msg = "next file number";+ }+ break;++ case kLastSequence:+ if (GetVarint64(&input, &last_sequence_)) {+ has_last_sequence_ = true;+ } else {+ msg = "last sequence number";+ }+ break;++ case kCompactPointer:+ if (GetLevel(&input, &level) &&+ GetInternalKey(&input, &key)) {+ compact_pointers_.push_back(std::make_pair(level, key));+ } else {+ msg = "compaction pointer";+ }+ break;++ case kDeletedFile:+ if (GetLevel(&input, &level) &&+ GetVarint64(&input, &number)) {+ deleted_files_.insert(std::make_pair(level, number));+ } else {+ msg = "deleted file";+ }+ break;++ case kNewFile:+ if (GetLevel(&input, &level) &&+ GetVarint64(&input, &f.number) &&+ GetVarint64(&input, &f.file_size) &&+ GetInternalKey(&input, &f.smallest) &&+ GetInternalKey(&input, &f.largest)) {+ new_files_.push_back(std::make_pair(level, f));+ } else {+ msg = "new-file entry";+ }+ break;++ default:+ msg = "unknown tag";+ break;+ }+ }++ if (msg == NULL && !input.empty()) {+ msg = "invalid tag";+ }++ Status result;+ if (msg != NULL) {+ result = Status::Corruption("VersionEdit", msg);+ }+ return result;+}++std::string VersionEdit::DebugString() const {+ std::string r;+ r.append("VersionEdit {");+ if (has_comparator_) {+ r.append("\n Comparator: ");+ r.append(comparator_);+ }+ if (has_log_number_) {+ r.append("\n LogNumber: ");+ AppendNumberTo(&r, log_number_);+ }+ if (has_prev_log_number_) {+ r.append("\n PrevLogNumber: ");+ AppendNumberTo(&r, prev_log_number_);+ }+ if (has_next_file_number_) {+ r.append("\n NextFile: ");+ AppendNumberTo(&r, next_file_number_);+ }+ if (has_last_sequence_) {+ r.append("\n LastSeq: ");+ AppendNumberTo(&r, last_sequence_);+ }+ for (size_t i = 0; i < compact_pointers_.size(); i++) {+ r.append("\n CompactPointer: ");+ AppendNumberTo(&r, compact_pointers_[i].first);+ r.append(" ");+ r.append(compact_pointers_[i].second.DebugString());+ }+ for (DeletedFileSet::const_iterator iter = deleted_files_.begin();+ iter != deleted_files_.end();+ ++iter) {+ r.append("\n DeleteFile: ");+ AppendNumberTo(&r, iter->first);+ r.append(" ");+ AppendNumberTo(&r, iter->second);+ }+ for (size_t i = 0; i < new_files_.size(); i++) {+ const FileMetaData& f = new_files_[i].second;+ r.append("\n AddFile: ");+ AppendNumberTo(&r, new_files_[i].first);+ r.append(" ");+ AppendNumberTo(&r, f.number);+ r.append(" ");+ AppendNumberTo(&r, f.file_size);+ r.append(" ");+ r.append(f.smallest.DebugString());+ r.append(" .. ");+ r.append(f.largest.DebugString());+ }+ r.append("\n}\n");+ return r;+}++} // namespace leveldb
+ cbits/leveldb/db/version_edit.h view
@@ -0,0 +1,107 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_VERSION_EDIT_H_+#define STORAGE_LEVELDB_DB_VERSION_EDIT_H_++#include <set>+#include <utility>+#include <vector>+#include "db/dbformat.h"++namespace leveldb {++class VersionSet;++struct FileMetaData {+ int refs;+ int allowed_seeks; // Seeks allowed until compaction+ uint64_t number;+ uint64_t file_size; // File size in bytes+ InternalKey smallest; // Smallest internal key served by table+ InternalKey largest; // Largest internal key served by table++ FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) { }+};++class VersionEdit {+ public:+ VersionEdit() { Clear(); }+ ~VersionEdit() { }++ void Clear();++ void SetComparatorName(const Slice& name) {+ has_comparator_ = true;+ comparator_ = name.ToString();+ }+ void SetLogNumber(uint64_t num) {+ has_log_number_ = true;+ log_number_ = num;+ }+ void SetPrevLogNumber(uint64_t num) {+ has_prev_log_number_ = true;+ prev_log_number_ = num;+ }+ void SetNextFile(uint64_t num) {+ has_next_file_number_ = true;+ next_file_number_ = num;+ }+ void SetLastSequence(SequenceNumber seq) {+ has_last_sequence_ = true;+ last_sequence_ = seq;+ }+ void SetCompactPointer(int level, const InternalKey& key) {+ compact_pointers_.push_back(std::make_pair(level, key));+ }++ // Add the specified file at the specified number.+ // REQUIRES: This version has not been saved (see VersionSet::SaveTo)+ // REQUIRES: "smallest" and "largest" are smallest and largest keys in file+ void AddFile(int level, uint64_t file,+ uint64_t file_size,+ const InternalKey& smallest,+ const InternalKey& largest) {+ FileMetaData f;+ f.number = file;+ f.file_size = file_size;+ f.smallest = smallest;+ f.largest = largest;+ new_files_.push_back(std::make_pair(level, f));+ }++ // Delete the specified "file" from the specified "level".+ void DeleteFile(int level, uint64_t file) {+ deleted_files_.insert(std::make_pair(level, file));+ }++ void EncodeTo(std::string* dst) const;+ Status DecodeFrom(const Slice& src);++ std::string DebugString() const;++ private:+ friend class VersionSet;++ typedef std::set< std::pair<int, uint64_t> > DeletedFileSet;++ std::string comparator_;+ uint64_t log_number_;+ uint64_t prev_log_number_;+ uint64_t next_file_number_;+ SequenceNumber last_sequence_;+ bool has_comparator_;+ bool has_log_number_;+ bool has_prev_log_number_;+ bool has_next_file_number_;+ bool has_last_sequence_;++ std::vector< std::pair<int, InternalKey> > compact_pointers_;+ DeletedFileSet deleted_files_;+ std::vector< std::pair<int, FileMetaData> > new_files_;+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_VERSION_EDIT_H_
+ cbits/leveldb/db/version_edit_test.cc view
@@ -0,0 +1,46 @@+// 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 "db/version_edit.h"+#include "util/testharness.h"++namespace leveldb {++static void TestEncodeDecode(const VersionEdit& edit) {+ std::string encoded, encoded2;+ edit.EncodeTo(&encoded);+ VersionEdit parsed;+ Status s = parsed.DecodeFrom(encoded);+ ASSERT_TRUE(s.ok()) << s.ToString();+ parsed.EncodeTo(&encoded2);+ ASSERT_EQ(encoded, encoded2);+}++class VersionEditTest { };++TEST(VersionEditTest, EncodeDecode) {+ static const uint64_t kBig = 1ull << 50;++ VersionEdit edit;+ for (int i = 0; i < 4; i++) {+ TestEncodeDecode(edit);+ edit.AddFile(3, kBig + 300 + i, kBig + 400 + i,+ InternalKey("foo", kBig + 500 + i, kTypeValue),+ InternalKey("zoo", kBig + 600 + i, kTypeDeletion));+ edit.DeleteFile(4, kBig + 700 + i);+ edit.SetCompactPointer(i, InternalKey("x", kBig + 900 + i, kTypeValue));+ }++ edit.SetComparatorName("foo");+ edit.SetLogNumber(kBig + 100);+ edit.SetNextFile(kBig + 200);+ edit.SetLastSequence(kBig + 1000);+ TestEncodeDecode(edit);+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/db/version_set.cc view
@@ -0,0 +1,1397 @@+// 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 "db/version_set.h"++#include <algorithm>+#include <stdio.h>+#include "db/filename.h"+#include "db/log_reader.h"+#include "db/log_writer.h"+#include "db/memtable.h"+#include "db/table_cache.h"+#include "leveldb/env.h"+#include "leveldb/table_builder.h"+#include "table/merger.h"+#include "table/two_level_iterator.h"+#include "util/coding.h"+#include "util/logging.h"++namespace leveldb {++static const int kTargetFileSize = 2 * 1048576;++// Maximum bytes of overlaps in grandparent (i.e., level+2) before we+// stop building a single file in a level->level+1 compaction.+static const int64_t kMaxGrandParentOverlapBytes = 10 * kTargetFileSize;++// Maximum number of bytes in all compacted files. We avoid expanding+// the lower level file set of a compaction if it would make the+// total compaction cover more than this many bytes.+static const int64_t kExpandedCompactionByteSizeLimit = 25 * kTargetFileSize;++static double MaxBytesForLevel(int level) {+ // Note: the result for level zero is not really used since we set+ // the level-0 compaction threshold based on number of files.+ double result = 10 * 1048576.0; // Result for both level-0 and level-1+ while (level > 1) {+ result *= 10;+ level--;+ }+ return result;+}++static uint64_t MaxFileSizeForLevel(int level) {+ return kTargetFileSize; // We could vary per level to reduce number of files?+}++static int64_t TotalFileSize(const std::vector<FileMetaData*>& files) {+ int64_t sum = 0;+ for (size_t i = 0; i < files.size(); i++) {+ sum += files[i]->file_size;+ }+ return sum;+}++namespace {+std::string IntSetToString(const std::set<uint64_t>& s) {+ std::string result = "{";+ for (std::set<uint64_t>::const_iterator it = s.begin();+ it != s.end();+ ++it) {+ result += (result.size() > 1) ? "," : "";+ result += NumberToString(*it);+ }+ result += "}";+ return result;+}+} // namespace++Version::~Version() {+ assert(refs_ == 0);++ // Remove from linked list+ prev_->next_ = next_;+ next_->prev_ = prev_;++ // Drop references to files+ for (int level = 0; level < config::kNumLevels; level++) {+ for (size_t i = 0; i < files_[level].size(); i++) {+ FileMetaData* f = files_[level][i];+ assert(f->refs > 0);+ f->refs--;+ if (f->refs <= 0) {+ delete f;+ }+ }+ }+}++int FindFile(const InternalKeyComparator& icmp,+ const std::vector<FileMetaData*>& files,+ const Slice& key) {+ uint32_t left = 0;+ uint32_t right = files.size();+ while (left < right) {+ uint32_t mid = (left + right) / 2;+ const FileMetaData* f = files[mid];+ if (icmp.InternalKeyComparator::Compare(f->largest.Encode(), key) < 0) {+ // Key at "mid.largest" is < "target". Therefore all+ // files at or before "mid" are uninteresting.+ left = mid + 1;+ } else {+ // Key at "mid.largest" is >= "target". Therefore all files+ // after "mid" are uninteresting.+ right = mid;+ }+ }+ return right;+}++static bool AfterFile(const Comparator* ucmp,+ const Slice* user_key, const FileMetaData* f) {+ // NULL user_key occurs before all keys and is therefore never after *f+ return (user_key != NULL &&+ ucmp->Compare(*user_key, f->largest.user_key()) > 0);+}++static bool BeforeFile(const Comparator* ucmp,+ const Slice* user_key, const FileMetaData* f) {+ // NULL user_key occurs after all keys and is therefore never before *f+ return (user_key != NULL &&+ ucmp->Compare(*user_key, f->smallest.user_key()) < 0);+}++bool SomeFileOverlapsRange(+ const InternalKeyComparator& icmp,+ bool disjoint_sorted_files,+ const std::vector<FileMetaData*>& files,+ const Slice* smallest_user_key,+ const Slice* largest_user_key) {+ const Comparator* ucmp = icmp.user_comparator();+ if (!disjoint_sorted_files) {+ // Need to check against all files+ for (int i = 0; i < files.size(); i++) {+ const FileMetaData* f = files[i];+ if (AfterFile(ucmp, smallest_user_key, f) ||+ BeforeFile(ucmp, largest_user_key, f)) {+ // No overlap+ } else {+ return true; // Overlap+ }+ }+ return false;+ }++ // Binary search over file list+ uint32_t index = 0;+ if (smallest_user_key != NULL) {+ // Find the earliest possible internal key for smallest_user_key+ InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek);+ index = FindFile(icmp, files, small.Encode());+ }++ if (index >= files.size()) {+ // beginning of range is after all files, so no overlap.+ return false;+ }++ return !BeforeFile(ucmp, largest_user_key, files[index]);+}++// An internal iterator. For a given version/level pair, yields+// information about the files in the level. For a given entry, key()+// is the largest key that occurs in the file, and value() is an+// 16-byte value containing the file number and file size, both+// encoded using EncodeFixed64.+class Version::LevelFileNumIterator : public Iterator {+ public:+ LevelFileNumIterator(const InternalKeyComparator& icmp,+ const std::vector<FileMetaData*>* flist)+ : icmp_(icmp),+ flist_(flist),+ index_(flist->size()) { // Marks as invalid+ }+ virtual bool Valid() const {+ return index_ < flist_->size();+ }+ virtual void Seek(const Slice& target) {+ index_ = FindFile(icmp_, *flist_, target);+ }+ virtual void SeekToFirst() { index_ = 0; }+ virtual void SeekToLast() {+ index_ = flist_->empty() ? 0 : flist_->size() - 1;+ }+ virtual void Next() {+ assert(Valid());+ index_++;+ }+ virtual void Prev() {+ assert(Valid());+ if (index_ == 0) {+ index_ = flist_->size(); // Marks as invalid+ } else {+ index_--;+ }+ }+ Slice key() const {+ assert(Valid());+ return (*flist_)[index_]->largest.Encode();+ }+ Slice value() const {+ assert(Valid());+ EncodeFixed64(value_buf_, (*flist_)[index_]->number);+ EncodeFixed64(value_buf_+8, (*flist_)[index_]->file_size);+ return Slice(value_buf_, sizeof(value_buf_));+ }+ virtual Status status() const { return Status::OK(); }+ private:+ const InternalKeyComparator icmp_;+ const std::vector<FileMetaData*>* const flist_;+ uint32_t index_;++ // Backing store for value(). Holds the file number and size.+ mutable char value_buf_[16];+};++static Iterator* GetFileIterator(void* arg,+ const ReadOptions& options,+ const Slice& file_value) {+ TableCache* cache = reinterpret_cast<TableCache*>(arg);+ if (file_value.size() != 16) {+ return NewErrorIterator(+ Status::Corruption("FileReader invoked with unexpected value"));+ } else {+ return cache->NewIterator(options,+ DecodeFixed64(file_value.data()),+ DecodeFixed64(file_value.data() + 8));+ }+}++Iterator* Version::NewConcatenatingIterator(const ReadOptions& options,+ int level) const {+ return NewTwoLevelIterator(+ new LevelFileNumIterator(vset_->icmp_, &files_[level]),+ &GetFileIterator, vset_->table_cache_, options);+}++void Version::AddIterators(const ReadOptions& options,+ std::vector<Iterator*>* iters) {+ // Merge all level zero files together since they may overlap+ for (size_t i = 0; i < files_[0].size(); i++) {+ iters->push_back(+ vset_->table_cache_->NewIterator(+ options, files_[0][i]->number, files_[0][i]->file_size));+ }++ // For levels > 0, we can use a concatenating iterator that sequentially+ // walks through the non-overlapping files in the level, opening them+ // lazily.+ for (int level = 1; level < config::kNumLevels; level++) {+ if (!files_[level].empty()) {+ iters->push_back(NewConcatenatingIterator(options, level));+ }+ }+}++// If "*iter" points at a value or deletion for user_key, store+// either the value, or a NotFound error and return true.+// Else return false.+static bool GetValue(const Comparator* cmp,+ Iterator* iter, const Slice& user_key,+ std::string* value,+ Status* s) {+ if (!iter->Valid()) {+ return false;+ }+ ParsedInternalKey parsed_key;+ if (!ParseInternalKey(iter->key(), &parsed_key)) {+ *s = Status::Corruption("corrupted key for ", user_key);+ return true;+ }+ if (cmp->Compare(parsed_key.user_key, user_key) != 0) {+ return false;+ }+ switch (parsed_key.type) {+ case kTypeDeletion:+ *s = Status::NotFound(Slice()); // Use an empty error message for speed+ break;+ case kTypeValue: {+ Slice v = iter->value();+ value->assign(v.data(), v.size());+ break;+ }+ }+ return true;+}++static bool NewestFirst(FileMetaData* a, FileMetaData* b) {+ return a->number > b->number;+}++Status Version::Get(const ReadOptions& options,+ const LookupKey& k,+ std::string* value,+ GetStats* stats) {+ Slice ikey = k.internal_key();+ Slice user_key = k.user_key();+ const Comparator* ucmp = vset_->icmp_.user_comparator();+ Status s;++ stats->seek_file = NULL;+ stats->seek_file_level = -1;+ FileMetaData* last_file_read = NULL;+ int last_file_read_level = -1;++ // We can search level-by-level since entries never hop across+ // levels. Therefore we are guaranteed that if we find data+ // in an smaller level, later levels are irrelevant.+ std::vector<FileMetaData*> tmp;+ FileMetaData* tmp2;+ for (int level = 0; level < config::kNumLevels; level++) {+ size_t num_files = files_[level].size();+ if (num_files == 0) continue;++ // Get the list of files to search in this level+ FileMetaData* const* files = &files_[level][0];+ if (level == 0) {+ // Level-0 files may overlap each other. Find all files that+ // overlap user_key and process them in order from newest to oldest.+ tmp.reserve(num_files);+ for (uint32_t i = 0; i < num_files; i++) {+ FileMetaData* f = files[i];+ if (ucmp->Compare(user_key, f->smallest.user_key()) >= 0 &&+ ucmp->Compare(user_key, f->largest.user_key()) <= 0) {+ tmp.push_back(f);+ }+ }+ if (tmp.empty()) continue;++ std::sort(tmp.begin(), tmp.end(), NewestFirst);+ files = &tmp[0];+ num_files = tmp.size();+ } else {+ // Binary search to find earliest index whose largest key >= ikey.+ uint32_t index = FindFile(vset_->icmp_, files_[level], ikey);+ if (index >= num_files) {+ files = NULL;+ num_files = 0;+ } else {+ tmp2 = files[index];+ if (ucmp->Compare(user_key, tmp2->smallest.user_key()) < 0) {+ // All of "tmp2" is past any data for user_key+ files = NULL;+ num_files = 0;+ } else {+ files = &tmp2;+ num_files = 1;+ }+ }+ }++ for (uint32_t i = 0; i < num_files; ++i) {+ if (last_file_read != NULL && stats->seek_file == NULL) {+ // We have had more than one seek for this read. Charge the 1st file.+ stats->seek_file = last_file_read;+ stats->seek_file_level = last_file_read_level;+ }++ FileMetaData* f = files[i];+ last_file_read = f;+ last_file_read_level = level;++ Iterator* iter = vset_->table_cache_->NewIterator(+ options,+ f->number,+ f->file_size);+ iter->Seek(ikey);+ const bool done = GetValue(ucmp, iter, user_key, value, &s);+ if (!iter->status().ok()) {+ s = iter->status();+ delete iter;+ return s;+ } else {+ delete iter;+ if (done) {+ return s;+ }+ }+ }+ }++ return Status::NotFound(Slice()); // Use an empty error message for speed+}++bool Version::UpdateStats(const GetStats& stats) {+ FileMetaData* f = stats.seek_file;+ if (f != NULL) {+ f->allowed_seeks--;+ if (f->allowed_seeks <= 0 && file_to_compact_ == NULL) {+ file_to_compact_ = f;+ file_to_compact_level_ = stats.seek_file_level;+ return true;+ }+ }+ return false;+}++void Version::Ref() {+ ++refs_;+}++void Version::Unref() {+ assert(this != &vset_->dummy_versions_);+ assert(refs_ >= 1);+ --refs_;+ if (refs_ == 0) {+ delete this;+ }+}++bool Version::OverlapInLevel(int level,+ const Slice* smallest_user_key,+ const Slice* largest_user_key) {+ return SomeFileOverlapsRange(vset_->icmp_, (level > 0), files_[level],+ smallest_user_key, largest_user_key);+}++int Version::PickLevelForMemTableOutput(+ const Slice& smallest_user_key,+ const Slice& largest_user_key) {+ int level = 0;+ if (!OverlapInLevel(0, &smallest_user_key, &largest_user_key)) {+ // Push to next level if there is no overlap in next level,+ // and the #bytes overlapping in the level after that are limited.+ InternalKey start(smallest_user_key, kMaxSequenceNumber, kValueTypeForSeek);+ InternalKey limit(largest_user_key, 0, static_cast<ValueType>(0));+ std::vector<FileMetaData*> overlaps;+ while (level < config::kMaxMemCompactLevel) {+ if (OverlapInLevel(level + 1, &smallest_user_key, &largest_user_key)) {+ break;+ }+ GetOverlappingInputs(level + 2, &start, &limit, &overlaps);+ const int64_t sum = TotalFileSize(overlaps);+ if (sum > kMaxGrandParentOverlapBytes) {+ break;+ }+ level++;+ }+ }+ return level;+}++// Store in "*inputs" all files in "level" that overlap [begin,end]+void Version::GetOverlappingInputs(+ int level,+ const InternalKey* begin,+ const InternalKey* end,+ std::vector<FileMetaData*>* inputs) {+ inputs->clear();+ Slice user_begin, user_end;+ if (begin != NULL) {+ user_begin = begin->user_key();+ }+ if (end != NULL) {+ user_end = end->user_key();+ }+ const Comparator* user_cmp = vset_->icmp_.user_comparator();+ for (size_t i = 0; i < files_[level].size(); ) {+ FileMetaData* f = files_[level][i++];+ const Slice file_start = f->smallest.user_key();+ const Slice file_limit = f->largest.user_key();+ if (begin != NULL && user_cmp->Compare(file_limit, user_begin) < 0) {+ // "f" is completely before specified range; skip it+ } else if (end != NULL && user_cmp->Compare(file_start, user_end) > 0) {+ // "f" is completely after specified range; skip it+ } else {+ inputs->push_back(f);+ if (level == 0) {+ // Level-0 files may overlap each other. So check if the newly+ // added file has expanded the range. If so, restart search.+ if (begin != NULL && user_cmp->Compare(file_start, user_begin) < 0) {+ user_begin = file_start;+ inputs->clear();+ i = 0;+ } else if (end != NULL && user_cmp->Compare(file_limit, user_end) > 0) {+ user_end = file_limit;+ inputs->clear();+ i = 0;+ }+ }+ }+ }+}++std::string Version::DebugString() const {+ std::string r;+ for (int level = 0; level < config::kNumLevels; level++) {+ // E.g.,+ // --- level 1 ---+ // 17:123['a' .. 'd']+ // 20:43['e' .. 'g']+ r.append("--- level ");+ AppendNumberTo(&r, level);+ r.append(" ---\n");+ const std::vector<FileMetaData*>& files = files_[level];+ for (size_t i = 0; i < files.size(); i++) {+ r.push_back(' ');+ AppendNumberTo(&r, files[i]->number);+ r.push_back(':');+ AppendNumberTo(&r, files[i]->file_size);+ r.append("[");+ r.append(files[i]->smallest.DebugString());+ r.append(" .. ");+ r.append(files[i]->largest.DebugString());+ r.append("]\n");+ }+ }+ return r;+}++// A helper class so we can efficiently apply a whole sequence+// of edits to a particular state without creating intermediate+// Versions that contain full copies of the intermediate state.+class VersionSet::Builder {+ private:+ // Helper to sort by v->files_[file_number].smallest+ struct BySmallestKey {+ const InternalKeyComparator* internal_comparator;++ bool operator()(FileMetaData* f1, FileMetaData* f2) const {+ int r = internal_comparator->Compare(f1->smallest, f2->smallest);+ if (r != 0) {+ return (r < 0);+ } else {+ // Break ties by file number+ return (f1->number < f2->number);+ }+ }+ };++ typedef std::set<FileMetaData*, BySmallestKey> FileSet;+ struct LevelState {+ std::set<uint64_t> deleted_files;+ FileSet* added_files;+ };++ VersionSet* vset_;+ Version* base_;+ LevelState levels_[config::kNumLevels];++ public:+ // Initialize a builder with the files from *base and other info from *vset+ Builder(VersionSet* vset, Version* base)+ : vset_(vset),+ base_(base) {+ base_->Ref();+ BySmallestKey cmp;+ cmp.internal_comparator = &vset_->icmp_;+ for (int level = 0; level < config::kNumLevels; level++) {+ levels_[level].added_files = new FileSet(cmp);+ }+ }++ ~Builder() {+ for (int level = 0; level < config::kNumLevels; level++) {+ const FileSet* added = levels_[level].added_files;+ std::vector<FileMetaData*> to_unref;+ to_unref.reserve(added->size());+ for (FileSet::const_iterator it = added->begin();+ it != added->end(); ++it) {+ to_unref.push_back(*it);+ }+ delete added;+ for (uint32_t i = 0; i < to_unref.size(); i++) {+ FileMetaData* f = to_unref[i];+ f->refs--;+ if (f->refs <= 0) {+ delete f;+ }+ }+ }+ base_->Unref();+ }++ // Apply all of the edits in *edit to the current state.+ void Apply(VersionEdit* edit) {+ // Update compaction pointers+ for (size_t i = 0; i < edit->compact_pointers_.size(); i++) {+ const int level = edit->compact_pointers_[i].first;+ vset_->compact_pointer_[level] =+ edit->compact_pointers_[i].second.Encode().ToString();+ }++ // Delete files+ const VersionEdit::DeletedFileSet& del = edit->deleted_files_;+ for (VersionEdit::DeletedFileSet::const_iterator iter = del.begin();+ iter != del.end();+ ++iter) {+ const int level = iter->first;+ const uint64_t number = iter->second;+ levels_[level].deleted_files.insert(number);+ }++ // Add new files+ for (size_t i = 0; i < edit->new_files_.size(); i++) {+ const int level = edit->new_files_[i].first;+ FileMetaData* f = new FileMetaData(edit->new_files_[i].second);+ f->refs = 1;++ // We arrange to automatically compact this file after+ // a certain number of seeks. Let's assume:+ // (1) One seek costs 10ms+ // (2) Writing or reading 1MB costs 10ms (100MB/s)+ // (3) A compaction of 1MB does 25MB of IO:+ // 1MB read from this level+ // 10-12MB read from next level (boundaries may be misaligned)+ // 10-12MB written to next level+ // This implies that 25 seeks cost the same as the compaction+ // of 1MB of data. I.e., one seek costs approximately the+ // same as the compaction of 40KB of data. We are a little+ // conservative and allow approximately one seek for every 16KB+ // of data before triggering a compaction.+ f->allowed_seeks = (f->file_size / 16384);+ if (f->allowed_seeks < 100) f->allowed_seeks = 100;++ levels_[level].deleted_files.erase(f->number);+ levels_[level].added_files->insert(f);+ }+ }++ // Save the current state in *v.+ void SaveTo(Version* v) {+ BySmallestKey cmp;+ cmp.internal_comparator = &vset_->icmp_;+ for (int level = 0; level < config::kNumLevels; level++) {+ // Merge the set of added files with the set of pre-existing files.+ // Drop any deleted files. Store the result in *v.+ const std::vector<FileMetaData*>& base_files = base_->files_[level];+ std::vector<FileMetaData*>::const_iterator base_iter = base_files.begin();+ std::vector<FileMetaData*>::const_iterator base_end = base_files.end();+ const FileSet* added = levels_[level].added_files;+ v->files_[level].reserve(base_files.size() + added->size());+ for (FileSet::const_iterator added_iter = added->begin();+ added_iter != added->end();+ ++added_iter) {+ // Add all smaller files listed in base_+ for (std::vector<FileMetaData*>::const_iterator bpos+ = std::upper_bound(base_iter, base_end, *added_iter, cmp);+ base_iter != bpos;+ ++base_iter) {+ MaybeAddFile(v, level, *base_iter);+ }++ MaybeAddFile(v, level, *added_iter);+ }++ // Add remaining base files+ for (; base_iter != base_end; ++base_iter) {+ MaybeAddFile(v, level, *base_iter);+ }++#ifndef NDEBUG+ // Make sure there is no overlap in levels > 0+ if (level > 0) {+ for (uint32_t i = 1; i < v->files_[level].size(); i++) {+ const InternalKey& prev_end = v->files_[level][i-1]->largest;+ const InternalKey& this_begin = v->files_[level][i]->smallest;+ if (vset_->icmp_.Compare(prev_end, this_begin) >= 0) {+ fprintf(stderr, "overlapping ranges in same level %s vs. %s\n",+ prev_end.DebugString().c_str(),+ this_begin.DebugString().c_str());+ abort();+ }+ }+ }+#endif+ }+ }++ void MaybeAddFile(Version* v, int level, FileMetaData* f) {+ if (levels_[level].deleted_files.count(f->number) > 0) {+ // File is deleted: do nothing+ } else {+ std::vector<FileMetaData*>* files = &v->files_[level];+ if (level > 0 && !files->empty()) {+ // Must not overlap+ assert(vset_->icmp_.Compare((*files)[files->size()-1]->largest,+ f->smallest) < 0);+ }+ f->refs++;+ files->push_back(f);+ }+ }+};++VersionSet::VersionSet(const std::string& dbname,+ const Options* options,+ TableCache* table_cache,+ const InternalKeyComparator* cmp)+ : env_(options->env),+ dbname_(dbname),+ options_(options),+ table_cache_(table_cache),+ icmp_(*cmp),+ next_file_number_(2),+ manifest_file_number_(0), // Filled by Recover()+ last_sequence_(0),+ log_number_(0),+ prev_log_number_(0),+ descriptor_file_(NULL),+ descriptor_log_(NULL),+ dummy_versions_(this),+ current_(NULL) {+ AppendVersion(new Version(this));+}++VersionSet::~VersionSet() {+ current_->Unref();+ assert(dummy_versions_.next_ == &dummy_versions_); // List must be empty+ delete descriptor_log_;+ delete descriptor_file_;+}++void VersionSet::AppendVersion(Version* v) {+ // Make "v" current+ assert(v->refs_ == 0);+ assert(v != current_);+ if (current_ != NULL) {+ current_->Unref();+ }+ current_ = v;+ v->Ref();++ // Append to linked list+ v->prev_ = dummy_versions_.prev_;+ v->next_ = &dummy_versions_;+ v->prev_->next_ = v;+ v->next_->prev_ = v;+}++Status VersionSet::LogAndApply(VersionEdit* edit, port::Mutex* mu) {+ if (edit->has_log_number_) {+ assert(edit->log_number_ >= log_number_);+ assert(edit->log_number_ < next_file_number_);+ } else {+ edit->SetLogNumber(log_number_);+ }++ if (!edit->has_prev_log_number_) {+ edit->SetPrevLogNumber(prev_log_number_);+ }++ edit->SetNextFile(next_file_number_);+ edit->SetLastSequence(last_sequence_);++ Version* v = new Version(this);+ {+ Builder builder(this, current_);+ builder.Apply(edit);+ builder.SaveTo(v);+ }+ Finalize(v);++ // Initialize new descriptor log file if necessary by creating+ // a temporary file that contains a snapshot of the current version.+ std::string new_manifest_file;+ Status s;+ if (descriptor_log_ == NULL) {+ // No reason to unlock *mu here since we only hit this path in the+ // first call to LogAndApply (when opening the database).+ assert(descriptor_file_ == NULL);+ new_manifest_file = DescriptorFileName(dbname_, manifest_file_number_);+ edit->SetNextFile(next_file_number_);+ s = env_->NewWritableFile(new_manifest_file, &descriptor_file_);+ if (s.ok()) {+ descriptor_log_ = new log::Writer(descriptor_file_);+ s = WriteSnapshot(descriptor_log_);+ }+ }++ // Unlock during expensive MANIFEST log write+ {+ mu->Unlock();++ // Write new record to MANIFEST log+ if (s.ok()) {+ std::string record;+ edit->EncodeTo(&record);+ s = descriptor_log_->AddRecord(record);+ if (s.ok()) {+ s = descriptor_file_->Sync();+ }+ }++ // If we just created a new descriptor file, install it by writing a+ // new CURRENT file that points to it.+ if (s.ok() && !new_manifest_file.empty()) {+ s = SetCurrentFile(env_, dbname_, manifest_file_number_);+ }++ mu->Lock();+ }++ // Install the new version+ if (s.ok()) {+ AppendVersion(v);+ log_number_ = edit->log_number_;+ prev_log_number_ = edit->prev_log_number_;+ } else {+ delete v;+ if (!new_manifest_file.empty()) {+ delete descriptor_log_;+ delete descriptor_file_;+ descriptor_log_ = NULL;+ descriptor_file_ = NULL;+ env_->DeleteFile(new_manifest_file);+ }+ }++ return s;+}++Status VersionSet::Recover() {+ struct LogReporter : public log::Reader::Reporter {+ Status* status;+ virtual void Corruption(size_t bytes, const Status& s) {+ if (this->status->ok()) *this->status = s;+ }+ };++ // Read "CURRENT" file, which contains a pointer to the current manifest file+ std::string current;+ Status s = ReadFileToString(env_, CurrentFileName(dbname_), ¤t);+ if (!s.ok()) {+ return s;+ }+ if (current.empty() || current[current.size()-1] != '\n') {+ return Status::Corruption("CURRENT file does not end with newline");+ }+ current.resize(current.size() - 1);++ std::string dscname = dbname_ + "/" + current;+ SequentialFile* file;+ s = env_->NewSequentialFile(dscname, &file);+ if (!s.ok()) {+ return s;+ }++ bool have_log_number = false;+ bool have_prev_log_number = false;+ bool have_next_file = false;+ bool have_last_sequence = false;+ uint64_t next_file = 0;+ uint64_t last_sequence = 0;+ uint64_t log_number = 0;+ uint64_t prev_log_number = 0;+ Builder builder(this, current_);++ {+ LogReporter reporter;+ reporter.status = &s;+ log::Reader reader(file, &reporter, true/*checksum*/, 0/*initial_offset*/);+ Slice record;+ std::string scratch;+ while (reader.ReadRecord(&record, &scratch) && s.ok()) {+ VersionEdit edit;+ s = edit.DecodeFrom(record);+ if (s.ok()) {+ if (edit.has_comparator_ &&+ edit.comparator_ != icmp_.user_comparator()->Name()) {+ s = Status::InvalidArgument(+ edit.comparator_ + "does not match existing comparator ",+ icmp_.user_comparator()->Name());+ }+ }++ if (s.ok()) {+ builder.Apply(&edit);+ }++ if (edit.has_log_number_) {+ log_number = edit.log_number_;+ have_log_number = true;+ }++ if (edit.has_prev_log_number_) {+ prev_log_number = edit.prev_log_number_;+ have_prev_log_number = true;+ }++ if (edit.has_next_file_number_) {+ next_file = edit.next_file_number_;+ have_next_file = true;+ }++ if (edit.has_last_sequence_) {+ last_sequence = edit.last_sequence_;+ have_last_sequence = true;+ }+ }+ }+ delete file;+ file = NULL;++ if (s.ok()) {+ if (!have_next_file) {+ s = Status::Corruption("no meta-nextfile entry in descriptor");+ } else if (!have_log_number) {+ s = Status::Corruption("no meta-lognumber entry in descriptor");+ } else if (!have_last_sequence) {+ s = Status::Corruption("no last-sequence-number entry in descriptor");+ }++ if (!have_prev_log_number) {+ prev_log_number = 0;+ }++ MarkFileNumberUsed(prev_log_number);+ MarkFileNumberUsed(log_number);+ }++ if (s.ok()) {+ Version* v = new Version(this);+ builder.SaveTo(v);+ // Install recovered version+ Finalize(v);+ AppendVersion(v);+ manifest_file_number_ = next_file;+ next_file_number_ = next_file + 1;+ last_sequence_ = last_sequence;+ log_number_ = log_number;+ prev_log_number_ = prev_log_number;+ }++ return s;+}++void VersionSet::MarkFileNumberUsed(uint64_t number) {+ if (next_file_number_ <= number) {+ next_file_number_ = number + 1;+ }+}++void VersionSet::Finalize(Version* v) {+ // Precomputed best level for next compaction+ int best_level = -1;+ double best_score = -1;++ for (int level = 0; level < config::kNumLevels-1; level++) {+ double score;+ if (level == 0) {+ // We treat level-0 specially by bounding the number of files+ // instead of number of bytes for two reasons:+ //+ // (1) With larger write-buffer sizes, it is nice not to do too+ // many level-0 compactions.+ //+ // (2) The files in level-0 are merged on every read and+ // therefore we wish to avoid too many files when the individual+ // file size is small (perhaps because of a small write-buffer+ // setting, or very high compression ratios, or lots of+ // overwrites/deletions).+ score = v->files_[level].size() /+ static_cast<double>(config::kL0_CompactionTrigger);+ } else {+ // Compute the ratio of current size to size limit.+ const uint64_t level_bytes = TotalFileSize(v->files_[level]);+ score = static_cast<double>(level_bytes) / MaxBytesForLevel(level);+ }++ if (score > best_score) {+ best_level = level;+ best_score = score;+ }+ }++ v->compaction_level_ = best_level;+ v->compaction_score_ = best_score;+}++Status VersionSet::WriteSnapshot(log::Writer* log) {+ // TODO: Break up into multiple records to reduce memory usage on recovery?++ // Save metadata+ VersionEdit edit;+ edit.SetComparatorName(icmp_.user_comparator()->Name());++ // Save compaction pointers+ for (int level = 0; level < config::kNumLevels; level++) {+ if (!compact_pointer_[level].empty()) {+ InternalKey key;+ key.DecodeFrom(compact_pointer_[level]);+ edit.SetCompactPointer(level, key);+ }+ }++ // Save files+ for (int level = 0; level < config::kNumLevels; level++) {+ const std::vector<FileMetaData*>& files = current_->files_[level];+ for (size_t i = 0; i < files.size(); i++) {+ const FileMetaData* f = files[i];+ edit.AddFile(level, f->number, f->file_size, f->smallest, f->largest);+ }+ }++ std::string record;+ edit.EncodeTo(&record);+ return log->AddRecord(record);+}++int VersionSet::NumLevelFiles(int level) const {+ assert(level >= 0);+ assert(level < config::kNumLevels);+ return current_->files_[level].size();+}++const char* VersionSet::LevelSummary(LevelSummaryStorage* scratch) const {+ // Update code if kNumLevels changes+ assert(config::kNumLevels == 7);+ snprintf(scratch->buffer, sizeof(scratch->buffer),+ "files[ %d %d %d %d %d %d %d ]",+ int(current_->files_[0].size()),+ int(current_->files_[1].size()),+ int(current_->files_[2].size()),+ int(current_->files_[3].size()),+ int(current_->files_[4].size()),+ int(current_->files_[5].size()),+ int(current_->files_[6].size()));+ return scratch->buffer;+}++uint64_t VersionSet::ApproximateOffsetOf(Version* v, const InternalKey& ikey) {+ uint64_t result = 0;+ for (int level = 0; level < config::kNumLevels; level++) {+ const std::vector<FileMetaData*>& files = v->files_[level];+ for (size_t i = 0; i < files.size(); i++) {+ if (icmp_.Compare(files[i]->largest, ikey) <= 0) {+ // Entire file is before "ikey", so just add the file size+ result += files[i]->file_size;+ } else if (icmp_.Compare(files[i]->smallest, ikey) > 0) {+ // Entire file is after "ikey", so ignore+ if (level > 0) {+ // Files other than level 0 are sorted by meta->smallest, so+ // no further files in this level will contain data for+ // "ikey".+ break;+ }+ } else {+ // "ikey" falls in the range for this table. Add the+ // approximate offset of "ikey" within the table.+ Table* tableptr;+ Iterator* iter = table_cache_->NewIterator(+ ReadOptions(), files[i]->number, files[i]->file_size, &tableptr);+ if (tableptr != NULL) {+ result += tableptr->ApproximateOffsetOf(ikey.Encode());+ }+ delete iter;+ }+ }+ }+ return result;+}++void VersionSet::AddLiveFiles(std::set<uint64_t>* live) {+ for (Version* v = dummy_versions_.next_;+ v != &dummy_versions_;+ v = v->next_) {+ for (int level = 0; level < config::kNumLevels; level++) {+ const std::vector<FileMetaData*>& files = v->files_[level];+ for (size_t i = 0; i < files.size(); i++) {+ live->insert(files[i]->number);+ }+ }+ }+}++int64_t VersionSet::NumLevelBytes(int level) const {+ assert(level >= 0);+ assert(level < config::kNumLevels);+ return TotalFileSize(current_->files_[level]);+}++int64_t VersionSet::MaxNextLevelOverlappingBytes() {+ int64_t result = 0;+ std::vector<FileMetaData*> overlaps;+ for (int level = 1; level < config::kNumLevels - 1; level++) {+ for (size_t i = 0; i < current_->files_[level].size(); i++) {+ const FileMetaData* f = current_->files_[level][i];+ current_->GetOverlappingInputs(level+1, &f->smallest, &f->largest,+ &overlaps);+ const int64_t sum = TotalFileSize(overlaps);+ if (sum > result) {+ result = sum;+ }+ }+ }+ return result;+}++// Stores the minimal range that covers all entries in inputs in+// *smallest, *largest.+// REQUIRES: inputs is not empty+void VersionSet::GetRange(const std::vector<FileMetaData*>& inputs,+ InternalKey* smallest,+ InternalKey* largest) {+ assert(!inputs.empty());+ smallest->Clear();+ largest->Clear();+ for (size_t i = 0; i < inputs.size(); i++) {+ FileMetaData* f = inputs[i];+ if (i == 0) {+ *smallest = f->smallest;+ *largest = f->largest;+ } else {+ if (icmp_.Compare(f->smallest, *smallest) < 0) {+ *smallest = f->smallest;+ }+ if (icmp_.Compare(f->largest, *largest) > 0) {+ *largest = f->largest;+ }+ }+ }+}++// Stores the minimal range that covers all entries in inputs1 and inputs2+// in *smallest, *largest.+// REQUIRES: inputs is not empty+void VersionSet::GetRange2(const std::vector<FileMetaData*>& inputs1,+ const std::vector<FileMetaData*>& inputs2,+ InternalKey* smallest,+ InternalKey* largest) {+ std::vector<FileMetaData*> all = inputs1;+ all.insert(all.end(), inputs2.begin(), inputs2.end());+ GetRange(all, smallest, largest);+}++Iterator* VersionSet::MakeInputIterator(Compaction* c) {+ ReadOptions options;+ options.verify_checksums = options_->paranoid_checks;+ options.fill_cache = false;++ // Level-0 files have to be merged together. For other levels,+ // we will make a concatenating iterator per level.+ // TODO(opt): use concatenating iterator for level-0 if there is no overlap+ const int space = (c->level() == 0 ? c->inputs_[0].size() + 1 : 2);+ Iterator** list = new Iterator*[space];+ int num = 0;+ for (int which = 0; which < 2; which++) {+ if (!c->inputs_[which].empty()) {+ if (c->level() + which == 0) {+ const std::vector<FileMetaData*>& files = c->inputs_[which];+ for (size_t i = 0; i < files.size(); i++) {+ list[num++] = table_cache_->NewIterator(+ options, files[i]->number, files[i]->file_size);+ }+ } else {+ // Create concatenating iterator for the files from this level+ list[num++] = NewTwoLevelIterator(+ new Version::LevelFileNumIterator(icmp_, &c->inputs_[which]),+ &GetFileIterator, table_cache_, options);+ }+ }+ }+ assert(num <= space);+ Iterator* result = NewMergingIterator(&icmp_, list, num);+ delete[] list;+ return result;+}++Compaction* VersionSet::PickCompaction() {+ Compaction* c;+ int level;++ // We prefer compactions triggered by too much data in a level over+ // the compactions triggered by seeks.+ const bool size_compaction = (current_->compaction_score_ >= 1);+ const bool seek_compaction = (current_->file_to_compact_ != NULL);+ if (size_compaction) {+ level = current_->compaction_level_;+ assert(level >= 0);+ assert(level+1 < config::kNumLevels);+ c = new Compaction(level);++ // Pick the first file that comes after compact_pointer_[level]+ for (size_t i = 0; i < current_->files_[level].size(); i++) {+ FileMetaData* f = current_->files_[level][i];+ if (compact_pointer_[level].empty() ||+ icmp_.Compare(f->largest.Encode(), compact_pointer_[level]) > 0) {+ c->inputs_[0].push_back(f);+ break;+ }+ }+ if (c->inputs_[0].empty()) {+ // Wrap-around to the beginning of the key space+ c->inputs_[0].push_back(current_->files_[level][0]);+ }+ } else if (seek_compaction) {+ level = current_->file_to_compact_level_;+ c = new Compaction(level);+ c->inputs_[0].push_back(current_->file_to_compact_);+ } else {+ return NULL;+ }++ c->input_version_ = current_;+ c->input_version_->Ref();++ // Files in level 0 may overlap each other, so pick up all overlapping ones+ if (level == 0) {+ InternalKey smallest, largest;+ GetRange(c->inputs_[0], &smallest, &largest);+ // Note that the next call will discard the file we placed in+ // c->inputs_[0] earlier and replace it with an overlapping set+ // which will include the picked file.+ current_->GetOverlappingInputs(0, &smallest, &largest, &c->inputs_[0]);+ assert(!c->inputs_[0].empty());+ }++ SetupOtherInputs(c);++ return c;+}++void VersionSet::SetupOtherInputs(Compaction* c) {+ const int level = c->level();+ InternalKey smallest, largest;+ GetRange(c->inputs_[0], &smallest, &largest);++ current_->GetOverlappingInputs(level+1, &smallest, &largest, &c->inputs_[1]);++ // Get entire range covered by compaction+ InternalKey all_start, all_limit;+ GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit);++ // See if we can grow the number of inputs in "level" without+ // changing the number of "level+1" files we pick up.+ if (!c->inputs_[1].empty()) {+ std::vector<FileMetaData*> expanded0;+ current_->GetOverlappingInputs(level, &all_start, &all_limit, &expanded0);+ const int64_t inputs0_size = TotalFileSize(c->inputs_[0]);+ const int64_t inputs1_size = TotalFileSize(c->inputs_[1]);+ const int64_t expanded0_size = TotalFileSize(expanded0);+ if (expanded0.size() > c->inputs_[0].size() &&+ inputs1_size + expanded0_size < kExpandedCompactionByteSizeLimit) {+ InternalKey new_start, new_limit;+ GetRange(expanded0, &new_start, &new_limit);+ std::vector<FileMetaData*> expanded1;+ current_->GetOverlappingInputs(level+1, &new_start, &new_limit,+ &expanded1);+ if (expanded1.size() == c->inputs_[1].size()) {+ Log(options_->info_log,+ "Expanding@%d %d+%d (%ld+%ld bytes) to %d+%d (%ld+%ld bytes)\n",+ level,+ int(c->inputs_[0].size()),+ int(c->inputs_[1].size()),+ long(inputs0_size), long(inputs1_size),+ int(expanded0.size()),+ int(expanded1.size()),+ long(expanded0_size), long(inputs1_size));+ smallest = new_start;+ largest = new_limit;+ c->inputs_[0] = expanded0;+ c->inputs_[1] = expanded1;+ GetRange2(c->inputs_[0], c->inputs_[1], &all_start, &all_limit);+ }+ }+ }++ // Compute the set of grandparent files that overlap this compaction+ // (parent == level+1; grandparent == level+2)+ if (level + 2 < config::kNumLevels) {+ current_->GetOverlappingInputs(level + 2, &all_start, &all_limit,+ &c->grandparents_);+ }++ if (false) {+ Log(options_->info_log, "Compacting %d '%s' .. '%s'",+ level,+ smallest.DebugString().c_str(),+ largest.DebugString().c_str());+ }++ // Update the place where we will do the next compaction for this level.+ // We update this immediately instead of waiting for the VersionEdit+ // to be applied so that if the compaction fails, we will try a different+ // key range next time.+ compact_pointer_[level] = largest.Encode().ToString();+ c->edit_.SetCompactPointer(level, largest);+}++Compaction* VersionSet::CompactRange(+ int level,+ const InternalKey* begin,+ const InternalKey* end) {+ std::vector<FileMetaData*> inputs;+ current_->GetOverlappingInputs(level, begin, end, &inputs);+ if (inputs.empty()) {+ return NULL;+ }++ // Avoid compacting too much in one shot in case the range is large.+ const uint64_t limit = MaxFileSizeForLevel(level);+ uint64_t total = 0;+ for (int i = 0; i < inputs.size(); i++) {+ uint64_t s = inputs[i]->file_size;+ total += s;+ if (total >= limit) {+ inputs.resize(i + 1);+ break;+ }+ }++ Compaction* c = new Compaction(level);+ c->input_version_ = current_;+ c->input_version_->Ref();+ c->inputs_[0] = inputs;+ SetupOtherInputs(c);+ return c;+}++Compaction::Compaction(int level)+ : level_(level),+ max_output_file_size_(MaxFileSizeForLevel(level)),+ input_version_(NULL),+ grandparent_index_(0),+ seen_key_(false),+ overlapped_bytes_(0) {+ for (int i = 0; i < config::kNumLevels; i++) {+ level_ptrs_[i] = 0;+ }+}++Compaction::~Compaction() {+ if (input_version_ != NULL) {+ input_version_->Unref();+ }+}++bool Compaction::IsTrivialMove() const {+ // Avoid a move if there is lots of overlapping grandparent data.+ // Otherwise, the move could create a parent file that will require+ // a very expensive merge later on.+ return (num_input_files(0) == 1 &&+ num_input_files(1) == 0 &&+ TotalFileSize(grandparents_) <= kMaxGrandParentOverlapBytes);+}++void Compaction::AddInputDeletions(VersionEdit* edit) {+ for (int which = 0; which < 2; which++) {+ for (size_t i = 0; i < inputs_[which].size(); i++) {+ edit->DeleteFile(level_ + which, inputs_[which][i]->number);+ }+ }+}++bool Compaction::IsBaseLevelForKey(const Slice& user_key) {+ // Maybe use binary search to find right entry instead of linear search?+ const Comparator* user_cmp = input_version_->vset_->icmp_.user_comparator();+ for (int lvl = level_ + 2; lvl < config::kNumLevels; lvl++) {+ const std::vector<FileMetaData*>& files = input_version_->files_[lvl];+ for (; level_ptrs_[lvl] < files.size(); ) {+ FileMetaData* f = files[level_ptrs_[lvl]];+ if (user_cmp->Compare(user_key, f->largest.user_key()) <= 0) {+ // We've advanced far enough+ if (user_cmp->Compare(user_key, f->smallest.user_key()) >= 0) {+ // Key falls in this file's range, so definitely not base level+ return false;+ }+ break;+ }+ level_ptrs_[lvl]++;+ }+ }+ return true;+}++bool Compaction::ShouldStopBefore(const Slice& internal_key) {+ // Scan to find earliest grandparent file that contains key.+ const InternalKeyComparator* icmp = &input_version_->vset_->icmp_;+ while (grandparent_index_ < grandparents_.size() &&+ icmp->Compare(internal_key,+ grandparents_[grandparent_index_]->largest.Encode()) > 0) {+ if (seen_key_) {+ overlapped_bytes_ += grandparents_[grandparent_index_]->file_size;+ }+ grandparent_index_++;+ }+ seen_key_ = true;++ if (overlapped_bytes_ > kMaxGrandParentOverlapBytes) {+ // Too much overlap for current output; start new output+ overlapped_bytes_ = 0;+ return true;+ } else {+ return false;+ }+}++void Compaction::ReleaseInputs() {+ if (input_version_ != NULL) {+ input_version_->Unref();+ input_version_ = NULL;+ }+}++} // namespace leveldb
+ cbits/leveldb/db/version_set.h view
@@ -0,0 +1,370 @@+// 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.+//+// The representation of a DBImpl consists of a set of Versions. The+// newest version is called "current". Older versions may be kept+// around to provide a consistent view to live iterators.+//+// Each Version keeps track of a set of Table files per level. The+// entire set of versions is maintained in a VersionSet.+//+// Version,VersionSet are thread-compatible, but require external+// synchronization on all accesses.++#ifndef STORAGE_LEVELDB_DB_VERSION_SET_H_+#define STORAGE_LEVELDB_DB_VERSION_SET_H_++#include <map>+#include <set>+#include <vector>+#include "db/dbformat.h"+#include "db/version_edit.h"+#include "port/port.h"++namespace leveldb {++namespace log { class Writer; }++class Compaction;+class Iterator;+class MemTable;+class TableBuilder;+class TableCache;+class Version;+class VersionSet;+class WritableFile;++// Return the smallest index i such that files[i]->largest >= key.+// Return files.size() if there is no such file.+// REQUIRES: "files" contains a sorted list of non-overlapping files.+extern int FindFile(const InternalKeyComparator& icmp,+ const std::vector<FileMetaData*>& files,+ const Slice& key);++// Returns true iff some file in "files" overlaps the user key range+// [*smallest,*largest].+// smallest==NULL represents a key smaller than all keys in the DB.+// largest==NULL represents a key largest than all keys in the DB.+// REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges+// in sorted order.+extern bool SomeFileOverlapsRange(+ const InternalKeyComparator& icmp,+ bool disjoint_sorted_files,+ const std::vector<FileMetaData*>& files,+ const Slice* smallest_user_key,+ const Slice* largest_user_key);++class Version {+ public:+ // Append to *iters a sequence of iterators that will+ // yield the contents of this Version when merged together.+ // REQUIRES: This version has been saved (see VersionSet::SaveTo)+ void AddIterators(const ReadOptions&, std::vector<Iterator*>* iters);++ // Lookup the value for key. If found, store it in *val and+ // return OK. Else return a non-OK status. Fills *stats.+ // REQUIRES: lock is not held+ struct GetStats {+ FileMetaData* seek_file;+ int seek_file_level;+ };+ Status Get(const ReadOptions&, const LookupKey& key, std::string* val,+ GetStats* stats);++ // Adds "stats" into the current state. Returns true if a new+ // compaction may need to be triggered, false otherwise.+ // REQUIRES: lock is held+ bool UpdateStats(const GetStats& stats);++ // Reference count management (so Versions do not disappear out from+ // under live iterators)+ void Ref();+ void Unref();++ void GetOverlappingInputs(+ int level,+ const InternalKey* begin, // NULL means before all keys+ const InternalKey* end, // NULL means after all keys+ std::vector<FileMetaData*>* inputs);++ // Returns true iff some file in the specified level overlaps+ // some part of [*smallest_user_key,*largest_user_key].+ // smallest_user_key==NULL represents a key smaller than all keys in the DB.+ // largest_user_key==NULL represents a key largest than all keys in the DB.+ bool OverlapInLevel(int level,+ const Slice* smallest_user_key,+ const Slice* largest_user_key);++ // Return the level at which we should place a new memtable compaction+ // result that covers the range [smallest_user_key,largest_user_key].+ int PickLevelForMemTableOutput(const Slice& smallest_user_key,+ const Slice& largest_user_key);++ int NumFiles(int level) const { return files_[level].size(); }++ // Return a human readable string that describes this version's contents.+ std::string DebugString() const;++ private:+ friend class Compaction;+ friend class VersionSet;++ class LevelFileNumIterator;+ Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const;++ VersionSet* vset_; // VersionSet to which this Version belongs+ Version* next_; // Next version in linked list+ Version* prev_; // Previous version in linked list+ int refs_; // Number of live refs to this version++ // List of files per level+ std::vector<FileMetaData*> files_[config::kNumLevels];++ // Next file to compact based on seek stats.+ FileMetaData* file_to_compact_;+ int file_to_compact_level_;++ // Level that should be compacted next and its compaction score.+ // Score < 1 means compaction is not strictly needed. These fields+ // are initialized by Finalize().+ double compaction_score_;+ int compaction_level_;++ explicit Version(VersionSet* vset)+ : vset_(vset), next_(this), prev_(this), refs_(0),+ file_to_compact_(NULL),+ file_to_compact_level_(-1),+ compaction_score_(-1),+ compaction_level_(-1) {+ }++ ~Version();++ // No copying allowed+ Version(const Version&);+ void operator=(const Version&);+};++class VersionSet {+ public:+ VersionSet(const std::string& dbname,+ const Options* options,+ TableCache* table_cache,+ const InternalKeyComparator*);+ ~VersionSet();++ // Apply *edit to the current version to form a new descriptor that+ // is both saved to persistent state and installed as the new+ // current version. Will release *mu while actually writing to the file.+ // REQUIRES: *mu is held on entry.+ // REQUIRES: no other thread concurrently calls LogAndApply()+ Status LogAndApply(VersionEdit* edit, port::Mutex* mu);++ // Recover the last saved descriptor from persistent storage.+ Status Recover();++ // Return the current version.+ Version* current() const { return current_; }++ // Return the current manifest file number+ uint64_t ManifestFileNumber() const { return manifest_file_number_; }++ // Allocate and return a new file number+ uint64_t NewFileNumber() { return next_file_number_++; }++ // Return the number of Table files at the specified level.+ int NumLevelFiles(int level) const;++ // Return the combined file size of all files at the specified level.+ int64_t NumLevelBytes(int level) const;++ // Return the last sequence number.+ uint64_t LastSequence() const { return last_sequence_; }++ // Set the last sequence number to s.+ void SetLastSequence(uint64_t s) {+ assert(s >= last_sequence_);+ last_sequence_ = s;+ }++ // Mark the specified file number as used.+ void MarkFileNumberUsed(uint64_t number);++ // Return the current log file number.+ uint64_t LogNumber() const { return log_number_; }++ // Return the log file number for the log file that is currently+ // being compacted, or zero if there is no such log file.+ uint64_t PrevLogNumber() const { return prev_log_number_; }++ // Pick level and inputs for a new compaction.+ // Returns NULL if there is no compaction to be done.+ // Otherwise returns a pointer to a heap-allocated object that+ // describes the compaction. Caller should delete the result.+ Compaction* PickCompaction();++ // Return a compaction object for compacting the range [begin,end] in+ // the specified level. Returns NULL if there is nothing in that+ // level that overlaps the specified range. Caller should delete+ // the result.+ Compaction* CompactRange(+ int level,+ const InternalKey* begin,+ const InternalKey* end);++ // Return the maximum overlapping data (in bytes) at next level for any+ // file at a level >= 1.+ int64_t MaxNextLevelOverlappingBytes();++ // Create an iterator that reads over the compaction inputs for "*c".+ // The caller should delete the iterator when no longer needed.+ Iterator* MakeInputIterator(Compaction* c);++ // Returns true iff some level needs a compaction.+ bool NeedsCompaction() const {+ Version* v = current_;+ return (v->compaction_score_ >= 1) || (v->file_to_compact_ != NULL);+ }++ // Add all files listed in any live version to *live.+ // May also mutate some internal state.+ void AddLiveFiles(std::set<uint64_t>* live);++ // Return the approximate offset in the database of the data for+ // "key" as of version "v".+ uint64_t ApproximateOffsetOf(Version* v, const InternalKey& key);++ // Return a human-readable short (single-line) summary of the number+ // of files per level. Uses *scratch as backing store.+ struct LevelSummaryStorage {+ char buffer[100];+ };+ const char* LevelSummary(LevelSummaryStorage* scratch) const;++ private:+ class Builder;++ friend class Compaction;+ friend class Version;++ void Finalize(Version* v);++ void GetRange(const std::vector<FileMetaData*>& inputs,+ InternalKey* smallest,+ InternalKey* largest);++ void GetRange2(const std::vector<FileMetaData*>& inputs1,+ const std::vector<FileMetaData*>& inputs2,+ InternalKey* smallest,+ InternalKey* largest);++ void SetupOtherInputs(Compaction* c);++ // Save current contents to *log+ Status WriteSnapshot(log::Writer* log);++ void AppendVersion(Version* v);++ Env* const env_;+ const std::string dbname_;+ const Options* const options_;+ TableCache* const table_cache_;+ const InternalKeyComparator icmp_;+ uint64_t next_file_number_;+ uint64_t manifest_file_number_;+ uint64_t last_sequence_;+ uint64_t log_number_;+ uint64_t prev_log_number_; // 0 or backing store for memtable being compacted++ // Opened lazily+ WritableFile* descriptor_file_;+ log::Writer* descriptor_log_;+ Version dummy_versions_; // Head of circular doubly-linked list of versions.+ Version* current_; // == dummy_versions_.prev_++ // Per-level key at which the next compaction at that level should start.+ // Either an empty string, or a valid InternalKey.+ std::string compact_pointer_[config::kNumLevels];++ // No copying allowed+ VersionSet(const VersionSet&);+ void operator=(const VersionSet&);+};++// A Compaction encapsulates information about a compaction.+class Compaction {+ public:+ ~Compaction();++ // Return the level that is being compacted. Inputs from "level"+ // and "level+1" will be merged to produce a set of "level+1" files.+ int level() const { return level_; }++ // Return the object that holds the edits to the descriptor done+ // by this compaction.+ VersionEdit* edit() { return &edit_; }++ // "which" must be either 0 or 1+ int num_input_files(int which) const { return inputs_[which].size(); }++ // Return the ith input file at "level()+which" ("which" must be 0 or 1).+ FileMetaData* input(int which, int i) const { return inputs_[which][i]; }++ // Maximum size of files to build during this compaction.+ uint64_t MaxOutputFileSize() const { return max_output_file_size_; }++ // Is this a trivial compaction that can be implemented by just+ // moving a single input file to the next level (no merging or splitting)+ bool IsTrivialMove() const;++ // Add all inputs to this compaction as delete operations to *edit.+ void AddInputDeletions(VersionEdit* edit);++ // Returns true if the information we have available guarantees that+ // the compaction is producing data in "level+1" for which no data exists+ // in levels greater than "level+1".+ bool IsBaseLevelForKey(const Slice& user_key);++ // Returns true iff we should stop building the current output+ // before processing "internal_key".+ bool ShouldStopBefore(const Slice& internal_key);++ // Release the input version for the compaction, once the compaction+ // is successful.+ void ReleaseInputs();++ private:+ friend class Version;+ friend class VersionSet;++ explicit Compaction(int level);++ int level_;+ uint64_t max_output_file_size_;+ Version* input_version_;+ VersionEdit edit_;++ // Each compaction reads inputs from "level_" and "level_+1"+ std::vector<FileMetaData*> inputs_[2]; // The two sets of inputs++ // State used to check for number of of overlapping grandparent files+ // (parent == level_ + 1, grandparent == level_ + 2)+ std::vector<FileMetaData*> grandparents_;+ size_t grandparent_index_; // Index in grandparent_starts_+ bool seen_key_; // Some output key has been seen+ int64_t overlapped_bytes_; // Bytes of overlap between current output+ // and grandparent files++ // State for implementing IsBaseLevelForKey++ // level_ptrs_ holds indices into input_version_->levels_: our state+ // is that we are positioned at one of the file ranges for each+ // higher level than the ones involved in this compaction (i.e. for+ // all L >= level_ + 2).+ size_t level_ptrs_[config::kNumLevels];+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_DB_VERSION_SET_H_
+ cbits/leveldb/db/version_set_test.cc view
@@ -0,0 +1,179 @@+// 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 "db/version_set.h"+#include "util/logging.h"+#include "util/testharness.h"+#include "util/testutil.h"++namespace leveldb {++class FindFileTest {+ public:+ std::vector<FileMetaData*> files_;+ bool disjoint_sorted_files_;++ FindFileTest() : disjoint_sorted_files_(true) { }++ ~FindFileTest() {+ for (int i = 0; i < files_.size(); i++) {+ delete files_[i];+ }+ }++ void Add(const char* smallest, const char* largest,+ SequenceNumber smallest_seq = 100,+ SequenceNumber largest_seq = 100) {+ FileMetaData* f = new FileMetaData;+ f->number = files_.size() + 1;+ f->smallest = InternalKey(smallest, smallest_seq, kTypeValue);+ f->largest = InternalKey(largest, largest_seq, kTypeValue);+ files_.push_back(f);+ }++ int Find(const char* key) {+ InternalKey target(key, 100, kTypeValue);+ InternalKeyComparator cmp(BytewiseComparator());+ return FindFile(cmp, files_, target.Encode());+ }++ bool Overlaps(const char* smallest, const char* largest) {+ InternalKeyComparator cmp(BytewiseComparator());+ Slice s(smallest != NULL ? smallest : "");+ Slice l(largest != NULL ? largest : "");+ return SomeFileOverlapsRange(cmp, disjoint_sorted_files_, files_,+ (smallest != NULL ? &s : NULL),+ (largest != NULL ? &l : NULL));+ }+};++TEST(FindFileTest, Empty) {+ ASSERT_EQ(0, Find("foo"));+ ASSERT_TRUE(! Overlaps("a", "z"));+ ASSERT_TRUE(! Overlaps(NULL, "z"));+ ASSERT_TRUE(! Overlaps("a", NULL));+ ASSERT_TRUE(! Overlaps(NULL, NULL));+}++TEST(FindFileTest, Single) {+ Add("p", "q");+ ASSERT_EQ(0, Find("a"));+ ASSERT_EQ(0, Find("p"));+ ASSERT_EQ(0, Find("p1"));+ ASSERT_EQ(0, Find("q"));+ ASSERT_EQ(1, Find("q1"));+ ASSERT_EQ(1, Find("z"));++ ASSERT_TRUE(! Overlaps("a", "b"));+ ASSERT_TRUE(! Overlaps("z1", "z2"));+ ASSERT_TRUE(Overlaps("a", "p"));+ ASSERT_TRUE(Overlaps("a", "q"));+ ASSERT_TRUE(Overlaps("a", "z"));+ ASSERT_TRUE(Overlaps("p", "p1"));+ ASSERT_TRUE(Overlaps("p", "q"));+ ASSERT_TRUE(Overlaps("p", "z"));+ ASSERT_TRUE(Overlaps("p1", "p2"));+ ASSERT_TRUE(Overlaps("p1", "z"));+ ASSERT_TRUE(Overlaps("q", "q"));+ ASSERT_TRUE(Overlaps("q", "q1"));++ ASSERT_TRUE(! Overlaps(NULL, "j"));+ ASSERT_TRUE(! Overlaps("r", NULL));+ ASSERT_TRUE(Overlaps(NULL, "p"));+ ASSERT_TRUE(Overlaps(NULL, "p1"));+ ASSERT_TRUE(Overlaps("q", NULL));+ ASSERT_TRUE(Overlaps(NULL, NULL));+}+++TEST(FindFileTest, Multiple) {+ Add("150", "200");+ Add("200", "250");+ Add("300", "350");+ Add("400", "450");+ ASSERT_EQ(0, Find("100"));+ ASSERT_EQ(0, Find("150"));+ ASSERT_EQ(0, Find("151"));+ ASSERT_EQ(0, Find("199"));+ ASSERT_EQ(0, Find("200"));+ ASSERT_EQ(1, Find("201"));+ ASSERT_EQ(1, Find("249"));+ ASSERT_EQ(1, Find("250"));+ ASSERT_EQ(2, Find("251"));+ ASSERT_EQ(2, Find("299"));+ ASSERT_EQ(2, Find("300"));+ ASSERT_EQ(2, Find("349"));+ ASSERT_EQ(2, Find("350"));+ ASSERT_EQ(3, Find("351"));+ ASSERT_EQ(3, Find("400"));+ ASSERT_EQ(3, Find("450"));+ ASSERT_EQ(4, Find("451"));++ ASSERT_TRUE(! Overlaps("100", "149"));+ ASSERT_TRUE(! Overlaps("251", "299"));+ ASSERT_TRUE(! Overlaps("451", "500"));+ ASSERT_TRUE(! Overlaps("351", "399"));++ ASSERT_TRUE(Overlaps("100", "150"));+ ASSERT_TRUE(Overlaps("100", "200"));+ ASSERT_TRUE(Overlaps("100", "300"));+ ASSERT_TRUE(Overlaps("100", "400"));+ ASSERT_TRUE(Overlaps("100", "500"));+ ASSERT_TRUE(Overlaps("375", "400"));+ ASSERT_TRUE(Overlaps("450", "450"));+ ASSERT_TRUE(Overlaps("450", "500"));+}++TEST(FindFileTest, MultipleNullBoundaries) {+ Add("150", "200");+ Add("200", "250");+ Add("300", "350");+ Add("400", "450");+ ASSERT_TRUE(! Overlaps(NULL, "149"));+ ASSERT_TRUE(! Overlaps("451", NULL));+ ASSERT_TRUE(Overlaps(NULL, NULL));+ ASSERT_TRUE(Overlaps(NULL, "150"));+ ASSERT_TRUE(Overlaps(NULL, "199"));+ ASSERT_TRUE(Overlaps(NULL, "200"));+ ASSERT_TRUE(Overlaps(NULL, "201"));+ ASSERT_TRUE(Overlaps(NULL, "400"));+ ASSERT_TRUE(Overlaps(NULL, "800"));+ ASSERT_TRUE(Overlaps("100", NULL));+ ASSERT_TRUE(Overlaps("200", NULL));+ ASSERT_TRUE(Overlaps("449", NULL));+ ASSERT_TRUE(Overlaps("450", NULL));+}++TEST(FindFileTest, OverlapSequenceChecks) {+ Add("200", "200", 5000, 3000);+ ASSERT_TRUE(! Overlaps("199", "199"));+ ASSERT_TRUE(! Overlaps("201", "300"));+ ASSERT_TRUE(Overlaps("200", "200"));+ ASSERT_TRUE(Overlaps("190", "200"));+ ASSERT_TRUE(Overlaps("200", "210"));+}++TEST(FindFileTest, OverlappingFiles) {+ Add("150", "600");+ Add("400", "500");+ disjoint_sorted_files_ = false;+ ASSERT_TRUE(! Overlaps("100", "149"));+ ASSERT_TRUE(! Overlaps("601", "700"));+ ASSERT_TRUE(Overlaps("100", "150"));+ ASSERT_TRUE(Overlaps("100", "200"));+ ASSERT_TRUE(Overlaps("100", "300"));+ ASSERT_TRUE(Overlaps("100", "400"));+ ASSERT_TRUE(Overlaps("100", "500"));+ ASSERT_TRUE(Overlaps("375", "400"));+ ASSERT_TRUE(Overlaps("450", "450"));+ ASSERT_TRUE(Overlaps("450", "500"));+ ASSERT_TRUE(Overlaps("450", "700"));+ ASSERT_TRUE(Overlaps("600", "700"));+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/db/write_batch.cc view
@@ -0,0 +1,138 @@+// 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.+//+// WriteBatch::rep_ :=+// sequence: fixed64+// count: fixed32+// data: record[count]+// record :=+// kTypeValue varstring varstring |+// kTypeDeletion varstring+// varstring :=+// len: varint32+// data: uint8[len]++#include "leveldb/write_batch.h"++#include "leveldb/db.h"+#include "db/dbformat.h"+#include "db/memtable.h"+#include "db/write_batch_internal.h"+#include "util/coding.h"++namespace leveldb {++WriteBatch::WriteBatch() {+ Clear();+}++WriteBatch::~WriteBatch() { }++WriteBatch::Handler::~Handler() { }++void WriteBatch::Clear() {+ rep_.clear();+ rep_.resize(12);+}++Status WriteBatch::Iterate(Handler* handler) const {+ Slice input(rep_);+ if (input.size() < 12) {+ return Status::Corruption("malformed WriteBatch (too small)");+ }++ input.remove_prefix(12);+ Slice key, value;+ int found = 0;+ while (!input.empty()) {+ found++;+ char tag = input[0];+ input.remove_prefix(1);+ switch (tag) {+ case kTypeValue:+ if (GetLengthPrefixedSlice(&input, &key) &&+ GetLengthPrefixedSlice(&input, &value)) {+ handler->Put(key, value);+ } else {+ return Status::Corruption("bad WriteBatch Put");+ }+ break;+ case kTypeDeletion:+ if (GetLengthPrefixedSlice(&input, &key)) {+ handler->Delete(key);+ } else {+ return Status::Corruption("bad WriteBatch Delete");+ }+ break;+ default:+ return Status::Corruption("unknown WriteBatch tag");+ }+ }+ if (found != WriteBatchInternal::Count(this)) {+ return Status::Corruption("WriteBatch has wrong count");+ } else {+ return Status::OK();+ }+}++int WriteBatchInternal::Count(const WriteBatch* b) {+ return DecodeFixed32(b->rep_.data() + 8);+}++void WriteBatchInternal::SetCount(WriteBatch* b, int n) {+ EncodeFixed32(&b->rep_[8], n);+}++SequenceNumber WriteBatchInternal::Sequence(const WriteBatch* b) {+ return SequenceNumber(DecodeFixed64(b->rep_.data()));+}++void WriteBatchInternal::SetSequence(WriteBatch* b, SequenceNumber seq) {+ EncodeFixed64(&b->rep_[0], seq);+}++void WriteBatch::Put(const Slice& key, const Slice& value) {+ WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1);+ rep_.push_back(static_cast<char>(kTypeValue));+ PutLengthPrefixedSlice(&rep_, key);+ PutLengthPrefixedSlice(&rep_, value);+}++void WriteBatch::Delete(const Slice& key) {+ WriteBatchInternal::SetCount(this, WriteBatchInternal::Count(this) + 1);+ rep_.push_back(static_cast<char>(kTypeDeletion));+ PutLengthPrefixedSlice(&rep_, key);+}++namespace {+class MemTableInserter : public WriteBatch::Handler {+ public:+ SequenceNumber sequence_;+ MemTable* mem_;++ virtual void Put(const Slice& key, const Slice& value) {+ mem_->Add(sequence_, kTypeValue, key, value);+ sequence_++;+ }+ virtual void Delete(const Slice& key) {+ mem_->Add(sequence_, kTypeDeletion, key, Slice());+ sequence_++;+ }+};+} // namespace++Status WriteBatchInternal::InsertInto(const WriteBatch* b,+ MemTable* memtable) {+ MemTableInserter inserter;+ inserter.sequence_ = WriteBatchInternal::Sequence(b);+ inserter.mem_ = memtable;+ return b->Iterate(&inserter);+}++void WriteBatchInternal::SetContents(WriteBatch* b, const Slice& contents) {+ assert(contents.size() >= 12);+ b->rep_.assign(contents.data(), contents.size());+}++} // namespace leveldb
+ cbits/leveldb/db/write_batch_internal.h view
@@ -0,0 +1,47 @@+// 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.++#ifndef STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_+#define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_++#include "leveldb/write_batch.h"++namespace leveldb {++class MemTable;++// WriteBatchInternal provides static methods for manipulating a+// WriteBatch that we don't want in the public WriteBatch interface.+class WriteBatchInternal {+ public:+ // Return the number of entries in the batch.+ static int Count(const WriteBatch* batch);++ // Set the count for the number of entries in the batch.+ static void SetCount(WriteBatch* batch, int n);++ // Return the seqeunce number for the start of this batch.+ static SequenceNumber Sequence(const WriteBatch* batch);++ // Store the specified number as the seqeunce number for the start of+ // this batch.+ static void SetSequence(WriteBatch* batch, SequenceNumber seq);++ static Slice Contents(const WriteBatch* batch) {+ return Slice(batch->rep_);+ }++ static size_t ByteSize(const WriteBatch* batch) {+ return batch->rep_.size();+ }++ static void SetContents(WriteBatch* batch, const Slice& contents);++ static Status InsertInto(const WriteBatch* batch, MemTable* memtable);+};++} // namespace leveldb+++#endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_
+ cbits/leveldb/db/write_batch_test.cc view
@@ -0,0 +1,89 @@+// 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/db.h"++#include "db/memtable.h"+#include "db/write_batch_internal.h"+#include "leveldb/env.h"+#include "util/logging.h"+#include "util/testharness.h"++namespace leveldb {++static std::string PrintContents(WriteBatch* b) {+ InternalKeyComparator cmp(BytewiseComparator());+ MemTable* mem = new MemTable(cmp);+ mem->Ref();+ std::string state;+ Status s = WriteBatchInternal::InsertInto(b, mem);+ Iterator* iter = mem->NewIterator();+ for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {+ ParsedInternalKey ikey;+ ASSERT_TRUE(ParseInternalKey(iter->key(), &ikey));+ switch (ikey.type) {+ case kTypeValue:+ state.append("Put(");+ state.append(ikey.user_key.ToString());+ state.append(", ");+ state.append(iter->value().ToString());+ state.append(")");+ break;+ case kTypeDeletion:+ state.append("Delete(");+ state.append(ikey.user_key.ToString());+ state.append(")");+ break;+ }+ state.append("@");+ state.append(NumberToString(ikey.sequence));+ }+ delete iter;+ if (!s.ok()) {+ state.append("ParseError()");+ }+ mem->Unref();+ return state;+}++class WriteBatchTest { };++TEST(WriteBatchTest, Empty) {+ WriteBatch batch;+ ASSERT_EQ("", PrintContents(&batch));+ ASSERT_EQ(0, WriteBatchInternal::Count(&batch));+}++TEST(WriteBatchTest, Multiple) {+ WriteBatch batch;+ batch.Put(Slice("foo"), Slice("bar"));+ batch.Delete(Slice("box"));+ batch.Put(Slice("baz"), Slice("boo"));+ WriteBatchInternal::SetSequence(&batch, 100);+ ASSERT_EQ(100, WriteBatchInternal::Sequence(&batch));+ ASSERT_EQ(3, WriteBatchInternal::Count(&batch));+ ASSERT_EQ("Put(baz, boo)@102"+ "Delete(box)@101"+ "Put(foo, bar)@100",+ PrintContents(&batch));+}++TEST(WriteBatchTest, Corruption) {+ WriteBatch batch;+ batch.Put(Slice("foo"), Slice("bar"));+ batch.Delete(Slice("box"));+ WriteBatchInternal::SetSequence(&batch, 200);+ Slice contents = WriteBatchInternal::Contents(&batch);+ WriteBatchInternal::SetContents(&batch,+ Slice(contents.data(),contents.size()-1));+ ASSERT_EQ("Put(foo, bar)@200"+ "ParseError()",+ PrintContents(&batch));+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/helpers/memenv/memenv.cc view
@@ -0,0 +1,374 @@+// 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 "helpers/memenv/memenv.h"++#include "leveldb/env.h"+#include "leveldb/status.h"+#include "port/port.h"+#include "util/mutexlock.h"+#include <map>+#include <string.h>+#include <string>+#include <vector>++namespace leveldb {++namespace {++class FileState {+ public:+ // FileStates are reference counted. The initial reference count is zero+ // and the caller must call Ref() at least once.+ FileState() : refs_(0), size_(0) {}++ // Increase the reference count.+ void Ref() {+ MutexLock lock(&refs_mutex_);+ ++refs_;+ }++ // Decrease the reference count. Delete if this is the last reference.+ void Unref() {+ bool do_delete = false;++ {+ MutexLock lock(&refs_mutex_);+ --refs_;+ assert(refs_ >= 0);+ if (refs_ <= 0) {+ do_delete = true;+ }+ }++ if (do_delete) {+ delete this;+ }+ }++ uint64_t Size() const { return size_; }++ Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {+ if (offset > size_) {+ return Status::IOError("Offset greater than file size.");+ }+ const uint64_t available = size_ - offset;+ if (n > available) {+ n = available;+ }+ if (n == 0) {+ *result = Slice();+ return Status::OK();+ }++ size_t block = offset / kBlockSize;+ size_t block_offset = offset % kBlockSize;++ if (n <= kBlockSize - block_offset) {+ // The requested bytes are all in the first block.+ *result = Slice(blocks_[block] + block_offset, n);+ return Status::OK();+ }++ size_t bytes_to_copy = n;+ char* dst = scratch;++ while (bytes_to_copy > 0) {+ size_t avail = kBlockSize - block_offset;+ if (avail > bytes_to_copy) {+ avail = bytes_to_copy;+ }+ memcpy(dst, blocks_[block] + block_offset, avail);++ bytes_to_copy -= avail;+ dst += avail;+ block++;+ block_offset = 0;+ }++ *result = Slice(scratch, n);+ return Status::OK();+ }++ Status Append(const Slice& data) {+ const char* src = data.data();+ size_t src_len = data.size();++ while (src_len > 0) {+ size_t avail;+ size_t offset = size_ % kBlockSize;++ if (offset != 0) {+ // There is some room in the last block.+ avail = kBlockSize - offset;+ } else {+ // No room in the last block; push new one.+ blocks_.push_back(new char[kBlockSize]);+ avail = kBlockSize;+ }++ if (avail > src_len) {+ avail = src_len;+ }+ memcpy(blocks_.back() + offset, src, avail);+ src_len -= avail;+ src += avail;+ size_ += avail;+ }++ return Status::OK();+ }++ private:+ // Private since only Unref() should be used to delete it.+ ~FileState() {+ for (std::vector<char*>::iterator i = blocks_.begin(); i != blocks_.end();+ ++i) {+ delete [] *i;+ }+ }++ // No copying allowed.+ FileState(const FileState&);+ void operator=(const FileState&);++ port::Mutex refs_mutex_;+ int refs_; // Protected by refs_mutex_;++ // The following fields are not protected by any mutex. They are only mutable+ // while the file is being written, and concurrent access is not allowed+ // to writable files.+ std::vector<char*> blocks_;+ uint64_t size_;++ enum { kBlockSize = 8 * 1024 };+};++class SequentialFileImpl : public SequentialFile {+ public:+ explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) {+ file_->Ref();+ }++ ~SequentialFileImpl() {+ file_->Unref();+ }++ virtual Status Read(size_t n, Slice* result, char* scratch) {+ Status s = file_->Read(pos_, n, result, scratch);+ if (s.ok()) {+ pos_ += result->size();+ }+ return s;+ }++ virtual Status Skip(uint64_t n) {+ if (pos_ > file_->Size()) {+ return Status::IOError("pos_ > file_->Size()");+ }+ const size_t available = file_->Size() - pos_;+ if (n > available) {+ n = available;+ }+ pos_ += n;+ return Status::OK();+ }++ private:+ FileState* file_;+ size_t pos_;+};++class RandomAccessFileImpl : public RandomAccessFile {+ public:+ explicit RandomAccessFileImpl(FileState* file) : file_(file) {+ file_->Ref();+ }++ ~RandomAccessFileImpl() {+ file_->Unref();+ }++ virtual Status Read(uint64_t offset, size_t n, Slice* result,+ char* scratch) const {+ return file_->Read(offset, n, result, scratch);+ }++ private:+ FileState* file_;+};++class WritableFileImpl : public WritableFile {+ public:+ WritableFileImpl(FileState* file) : file_(file) {+ file_->Ref();+ }++ ~WritableFileImpl() {+ file_->Unref();+ }++ virtual Status Append(const Slice& data) {+ return file_->Append(data);+ }++ virtual Status Close() { return Status::OK(); }+ virtual Status Flush() { return Status::OK(); }+ virtual Status Sync() { return Status::OK(); }++ private:+ FileState* file_;+};++class InMemoryEnv : public EnvWrapper {+ public:+ explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) { }++ virtual ~InMemoryEnv() {+ for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){+ i->second->Unref();+ }+ }++ // Partial implementation of the Env interface.+ virtual Status NewSequentialFile(const std::string& fname,+ SequentialFile** result) {+ MutexLock lock(&mutex_);+ if (file_map_.find(fname) == file_map_.end()) {+ *result = NULL;+ return Status::IOError(fname, "File not found");+ }++ *result = new SequentialFileImpl(file_map_[fname]);+ return Status::OK();+ }++ virtual Status NewRandomAccessFile(const std::string& fname,+ RandomAccessFile** result) {+ MutexLock lock(&mutex_);+ if (file_map_.find(fname) == file_map_.end()) {+ *result = NULL;+ return Status::IOError(fname, "File not found");+ }++ *result = new RandomAccessFileImpl(file_map_[fname]);+ return Status::OK();+ }++ virtual Status NewWritableFile(const std::string& fname,+ WritableFile** result) {+ MutexLock lock(&mutex_);+ if (file_map_.find(fname) != file_map_.end()) {+ DeleteFileInternal(fname);+ }++ FileState* file = new FileState();+ file->Ref();+ file_map_[fname] = file;++ *result = new WritableFileImpl(file);+ return Status::OK();+ }++ virtual bool FileExists(const std::string& fname) {+ MutexLock lock(&mutex_);+ return file_map_.find(fname) != file_map_.end();+ }++ virtual Status GetChildren(const std::string& dir,+ std::vector<std::string>* result) {+ MutexLock lock(&mutex_);+ result->clear();++ for (FileSystem::iterator i = file_map_.begin(); i != file_map_.end(); ++i){+ const std::string& filename = i->first;++ if (filename.size() >= dir.size() + 1 && filename[dir.size()] == '/' &&+ Slice(filename).starts_with(Slice(dir))) {+ result->push_back(filename.substr(dir.size() + 1));+ }+ }++ return Status::OK();+ }++ void DeleteFileInternal(const std::string& fname) {+ if (file_map_.find(fname) == file_map_.end()) {+ return;+ }++ file_map_[fname]->Unref();+ file_map_.erase(fname);+ }++ virtual Status DeleteFile(const std::string& fname) {+ MutexLock lock(&mutex_);+ if (file_map_.find(fname) == file_map_.end()) {+ return Status::IOError(fname, "File not found");+ }++ DeleteFileInternal(fname);+ return Status::OK();+ }++ virtual Status CreateDir(const std::string& dirname) {+ return Status::OK();+ }++ virtual Status DeleteDir(const std::string& dirname) {+ return Status::OK();+ }++ virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) {+ MutexLock lock(&mutex_);+ if (file_map_.find(fname) == file_map_.end()) {+ return Status::IOError(fname, "File not found");+ }++ *file_size = file_map_[fname]->Size();+ return Status::OK();+ }++ virtual Status RenameFile(const std::string& src,+ const std::string& target) {+ MutexLock lock(&mutex_);+ if (file_map_.find(src) == file_map_.end()) {+ return Status::IOError(src, "File not found");+ }++ DeleteFileInternal(target);+ file_map_[target] = file_map_[src];+ file_map_.erase(src);+ return Status::OK();+ }++ virtual Status LockFile(const std::string& fname, FileLock** lock) {+ *lock = new FileLock;+ return Status::OK();+ }++ virtual Status UnlockFile(FileLock* lock) {+ delete lock;+ return Status::OK();+ }++ virtual Status GetTestDirectory(std::string* path) {+ *path = "/test";+ return Status::OK();+ }++ private:+ // Map from filenames to FileState objects, representing a simple file system.+ typedef std::map<std::string, FileState*> FileSystem;+ port::Mutex mutex_;+ FileSystem file_map_; // Protected by mutex_.+};++} // namespace++Env* NewMemEnv(Env* base_env) {+ return new InMemoryEnv(base_env);+}++} // namespace leveldb
+ cbits/leveldb/helpers/memenv/memenv.h view
@@ -0,0 +1,20 @@+// 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.++#ifndef STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_+#define STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_++namespace leveldb {++class Env;++// Returns a new environment that stores its data in memory and delegates+// all non-file-storage tasks to base_env. The caller must delete the result+// when it is no longer needed.+// *base_env must remain live while the result is in use.+Env* NewMemEnv(Env* base_env);++} // namespace leveldb++#endif // STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_
+ cbits/leveldb/helpers/memenv/memenv_test.cc view
@@ -0,0 +1,232 @@+// 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 "helpers/memenv/memenv.h"++#include "db/db_impl.h"+#include "leveldb/db.h"+#include "leveldb/env.h"+#include "util/testharness.h"+#include <string>+#include <vector>++namespace leveldb {++class MemEnvTest {+ public:+ Env* env_;++ MemEnvTest()+ : env_(NewMemEnv(Env::Default())) {+ }+ ~MemEnvTest() {+ delete env_;+ }+};++TEST(MemEnvTest, Basics) {+ uint64_t file_size;+ WritableFile* writable_file;+ std::vector<std::string> children;++ ASSERT_OK(env_->CreateDir("/dir"));++ // Check that the directory is empty.+ ASSERT_TRUE(!env_->FileExists("/dir/non_existent"));+ ASSERT_TRUE(!env_->GetFileSize("/dir/non_existent", &file_size).ok());+ ASSERT_OK(env_->GetChildren("/dir", &children));+ ASSERT_EQ(0, children.size());++ // Create a file.+ ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));+ delete writable_file;++ // Check that the file exists.+ ASSERT_TRUE(env_->FileExists("/dir/f"));+ ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));+ ASSERT_EQ(0, file_size);+ ASSERT_OK(env_->GetChildren("/dir", &children));+ ASSERT_EQ(1, children.size());+ ASSERT_EQ("f", children[0]);++ // Write to the file.+ ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));+ ASSERT_OK(writable_file->Append("abc"));+ delete writable_file;++ // Check for expected size.+ ASSERT_OK(env_->GetFileSize("/dir/f", &file_size));+ ASSERT_EQ(3, file_size);++ // Check that renaming works.+ ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok());+ ASSERT_OK(env_->RenameFile("/dir/f", "/dir/g"));+ ASSERT_TRUE(!env_->FileExists("/dir/f"));+ ASSERT_TRUE(env_->FileExists("/dir/g"));+ ASSERT_OK(env_->GetFileSize("/dir/g", &file_size));+ ASSERT_EQ(3, file_size);++ // Check that opening non-existent file fails.+ SequentialFile* seq_file;+ RandomAccessFile* rand_file;+ ASSERT_TRUE(!env_->NewSequentialFile("/dir/non_existent", &seq_file).ok());+ ASSERT_TRUE(!seq_file);+ ASSERT_TRUE(!env_->NewRandomAccessFile("/dir/non_existent", &rand_file).ok());+ ASSERT_TRUE(!rand_file);++ // Check that deleting works.+ ASSERT_TRUE(!env_->DeleteFile("/dir/non_existent").ok());+ ASSERT_OK(env_->DeleteFile("/dir/g"));+ ASSERT_TRUE(!env_->FileExists("/dir/g"));+ ASSERT_OK(env_->GetChildren("/dir", &children));+ ASSERT_EQ(0, children.size());+ ASSERT_OK(env_->DeleteDir("/dir"));+}++TEST(MemEnvTest, ReadWrite) {+ WritableFile* writable_file;+ SequentialFile* seq_file;+ RandomAccessFile* rand_file;+ Slice result;+ char scratch[100];++ ASSERT_OK(env_->CreateDir("/dir"));++ ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));+ ASSERT_OK(writable_file->Append("hello "));+ ASSERT_OK(writable_file->Append("world"));+ delete writable_file;++ // Read sequentially.+ ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file));+ ASSERT_OK(seq_file->Read(5, &result, scratch)); // Read "hello".+ ASSERT_EQ(0, result.compare("hello"));+ ASSERT_OK(seq_file->Skip(1));+ ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Read "world".+ ASSERT_EQ(0, result.compare("world"));+ ASSERT_OK(seq_file->Read(1000, &result, scratch)); // Try reading past EOF.+ ASSERT_EQ(0, result.size());+ ASSERT_OK(seq_file->Skip(100)); // Try to skip past end of file.+ ASSERT_OK(seq_file->Read(1000, &result, scratch));+ ASSERT_EQ(0, result.size());+ delete seq_file;++ // Random reads.+ ASSERT_OK(env_->NewRandomAccessFile("/dir/f", &rand_file));+ ASSERT_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world".+ ASSERT_EQ(0, result.compare("world"));+ ASSERT_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello".+ ASSERT_EQ(0, result.compare("hello"));+ ASSERT_OK(rand_file->Read(10, 100, &result, scratch)); // Read "d".+ ASSERT_EQ(0, result.compare("d"));++ // Too high offset.+ ASSERT_TRUE(!rand_file->Read(1000, 5, &result, scratch).ok());+ delete rand_file;+}++TEST(MemEnvTest, Locks) {+ FileLock* lock;++ // These are no-ops, but we test they return success.+ ASSERT_OK(env_->LockFile("some file", &lock));+ ASSERT_OK(env_->UnlockFile(lock));+}++TEST(MemEnvTest, Misc) {+ std::string test_dir;+ ASSERT_OK(env_->GetTestDirectory(&test_dir));+ ASSERT_TRUE(!test_dir.empty());++ WritableFile* writable_file;+ ASSERT_OK(env_->NewWritableFile("/a/b", &writable_file));++ // These are no-ops, but we test they return success.+ ASSERT_OK(writable_file->Sync());+ ASSERT_OK(writable_file->Flush());+ ASSERT_OK(writable_file->Close());+ delete writable_file;+}++TEST(MemEnvTest, LargeWrite) {+ const size_t kWriteSize = 300 * 1024;+ char* scratch = new char[kWriteSize * 2];++ std::string write_data;+ for (size_t i = 0; i < kWriteSize; ++i) {+ write_data.append(1, static_cast<char>(i));+ }++ WritableFile* writable_file;+ ASSERT_OK(env_->NewWritableFile("/dir/f", &writable_file));+ ASSERT_OK(writable_file->Append("foo"));+ ASSERT_OK(writable_file->Append(write_data));+ delete writable_file;++ SequentialFile* seq_file;+ Slice result;+ ASSERT_OK(env_->NewSequentialFile("/dir/f", &seq_file));+ ASSERT_OK(seq_file->Read(3, &result, scratch)); // Read "foo".+ ASSERT_EQ(0, result.compare("foo"));++ size_t read = 0;+ std::string read_data;+ while (read < kWriteSize) {+ ASSERT_OK(seq_file->Read(kWriteSize - read, &result, scratch));+ read_data.append(result.data(), result.size());+ read += result.size();+ }+ ASSERT_TRUE(write_data == read_data);+ delete seq_file;+ delete [] scratch;+}++TEST(MemEnvTest, DBTest) {+ Options options;+ options.create_if_missing = true;+ options.env = env_;+ DB* db;++ const Slice keys[] = {Slice("aaa"), Slice("bbb"), Slice("ccc")};+ const Slice vals[] = {Slice("foo"), Slice("bar"), Slice("baz")};++ ASSERT_OK(DB::Open(options, "/dir/db", &db));+ for (size_t i = 0; i < 3; ++i) {+ ASSERT_OK(db->Put(WriteOptions(), keys[i], vals[i]));+ }++ for (size_t i = 0; i < 3; ++i) {+ std::string res;+ ASSERT_OK(db->Get(ReadOptions(), keys[i], &res));+ ASSERT_TRUE(res == vals[i]);+ }++ Iterator* iterator = db->NewIterator(ReadOptions());+ iterator->SeekToFirst();+ for (size_t i = 0; i < 3; ++i) {+ ASSERT_TRUE(iterator->Valid());+ ASSERT_TRUE(keys[i] == iterator->key());+ ASSERT_TRUE(vals[i] == iterator->value());+ iterator->Next();+ }+ ASSERT_TRUE(!iterator->Valid());+ delete iterator;++ DBImpl* dbi = reinterpret_cast<DBImpl*>(db);+ ASSERT_OK(dbi->TEST_CompactMemTable());++ for (size_t i = 0; i < 3; ++i) {+ std::string res;+ ASSERT_OK(db->Get(ReadOptions(), keys[i], &res));+ ASSERT_TRUE(res == vals[i]);+ }++ delete db;+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/port/atomic_pointer.h view
@@ -0,0 +1,144 @@+// 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.++// AtomicPointer provides storage for a lock-free pointer.+// Platform-dependent implementation of AtomicPointer:+// - If the platform provides a cheap barrier, we use it with raw pointers+// - If cstdatomic is present (on newer versions of gcc, it is), we use+// a cstdatomic-based AtomicPointer. However we prefer the memory+// barrier based version, because at least on a gcc 4.4 32-bit build+// on linux, we have encountered a buggy <cstdatomic>+// implementation. Also, some <cstdatomic> implementations are much+// slower than a memory-barrier based implementation (~16ns for+// <cstdatomic> based acquire-load vs. ~1ns for a barrier based+// acquire-load).+// This code is based on atomicops-internals-* in Google's perftools:+// http://code.google.com/p/google-perftools/source/browse/#svn%2Ftrunk%2Fsrc%2Fbase++#ifndef PORT_ATOMIC_POINTER_H_+#define PORT_ATOMIC_POINTER_H_++#include <stdint.h>+#ifdef LEVELDB_CSTDATOMIC_PRESENT+#include <cstdatomic>+#endif+#ifdef OS_WIN+#include <windows.h>+#endif+#ifdef OS_MACOSX+#include <libkern/OSAtomic.h>+#endif++#if defined(_M_X64) || defined(__x86_64__)+#define ARCH_CPU_X86_FAMILY 1+#elif defined(_M_IX86) || defined(__i386__) || defined(__i386)+#define ARCH_CPU_X86_FAMILY 1+#elif defined(__ARMEL__)+#define ARCH_CPU_ARM_FAMILY 1+#endif++namespace leveldb {+namespace port {++// Define MemoryBarrier() if available+// Windows on x86+#if defined(OS_WIN) && defined(COMPILER_MSVC) && defined(ARCH_CPU_X86_FAMILY)+// windows.h already provides a MemoryBarrier(void) macro+// http://msdn.microsoft.com/en-us/library/ms684208(v=vs.85).aspx+#define LEVELDB_HAVE_MEMORY_BARRIER++// Gcc on x86+#elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__)+inline void MemoryBarrier() {+ // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on+ // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering.+ __asm__ __volatile__("" : : : "memory");+}+#define LEVELDB_HAVE_MEMORY_BARRIER++// Sun Studio+#elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC)+inline void MemoryBarrier() {+ // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on+ // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering.+ asm volatile("" : : : "memory");+}+#define LEVELDB_HAVE_MEMORY_BARRIER++// Mac OS+#elif defined(OS_MACOSX)+inline void MemoryBarrier() {+ OSMemoryBarrier();+}+#define LEVELDB_HAVE_MEMORY_BARRIER++// ARM+#elif defined(ARCH_CPU_ARM_FAMILY)+typedef void (*LinuxKernelMemoryBarrierFunc)(void);+LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier __attribute__((weak)) =+ (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;+inline void MemoryBarrier() {+ pLinuxKernelMemoryBarrier();+}+#define LEVELDB_HAVE_MEMORY_BARRIER++#endif++// AtomicPointer built using platform-specific MemoryBarrier()+#if defined(LEVELDB_HAVE_MEMORY_BARRIER)+class AtomicPointer {+ private:+ void* rep_;+ public:+ AtomicPointer() { }+ explicit AtomicPointer(void* p) : rep_(p) {}+ inline void* NoBarrier_Load() const { return rep_; }+ inline void NoBarrier_Store(void* v) { rep_ = v; }+ inline void* Acquire_Load() const {+ void* result = rep_;+ MemoryBarrier();+ return result;+ }+ inline void Release_Store(void* v) {+ MemoryBarrier();+ rep_ = v;+ }+};++// AtomicPointer based on <cstdatomic>+#elif defined(LEVELDB_CSTDATOMIC_PRESENT)+class AtomicPointer {+ private:+ std::atomic<void*> rep_;+ public:+ AtomicPointer() { }+ explicit AtomicPointer(void* v) : rep_(v) { }+ inline void* Acquire_Load() const {+ return rep_.load(std::memory_order_acquire);+ }+ inline void Release_Store(void* v) {+ rep_.store(v, std::memory_order_release);+ }+ inline void* NoBarrier_Load() const {+ return rep_.load(std::memory_order_relaxed);+ }+ inline void NoBarrier_Store(void* v) {+ rep_.store(v, std::memory_order_relaxed);+ }+};++// We have neither MemoryBarrier(), nor <cstdatomic>+#else+#error Please implement AtomicPointer for this platform.++#endif++#undef LEVELDB_HAVE_MEMORY_BARRIER+#undef ARCH_CPU_X86_FAMILY+#undef ARCH_CPU_ARM_FAMILY++} // namespace port+} // namespace leveldb++#endif // PORT_ATOMIC_POINTER_H_
+ cbits/leveldb/port/port.h view
@@ -0,0 +1,21 @@+// 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.++#ifndef STORAGE_LEVELDB_PORT_PORT_H_+#define STORAGE_LEVELDB_PORT_PORT_H_++#include <string.h>++// Include the appropriate platform specific file below. If you are+// porting to a new platform, see "port_example.h" for documentation+// of what the new port_<platform>.h file must provide.+#if defined(LEVELDB_PLATFORM_POSIX)+# include "port/port_posix.h"+#elif defined(LEVELDB_PLATFORM_CHROMIUM)+# include "port/port_chromium.h"+#elif defined(LEVELDB_PLATFORM_ANDROID)+# include "port/port_android.h"+#endif++#endif // STORAGE_LEVELDB_PORT_PORT_H_
+ cbits/leveldb/port/port_android.cc view
@@ -0,0 +1,64 @@+// 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 "port/port_android.h"++#include <cstdlib>++extern "C" {+size_t fread_unlocked(void *a, size_t b, size_t c, FILE *d) {+ return fread(a, b, c, d);+}++size_t fwrite_unlocked(const void *a, size_t b, size_t c, FILE *d) {+ return fwrite(a, b, c, d);+}++int fflush_unlocked(FILE *f) {+ return fflush(f);+}++int fdatasync(int fd) {+ return fsync(fd);+}+}++namespace leveldb {+namespace port {++static void PthreadCall(const char* label, int result) {+ if (result != 0) {+ fprintf(stderr, "pthread %s: %s\n", label, strerror(result));+ abort();+ }+}++Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); }+Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); }+void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); }+void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); }++CondVar::CondVar(Mutex* mu)+ : mu_(mu) {+ PthreadCall("init cv", pthread_cond_init(&cv_, NULL));+}++CondVar::~CondVar() { + PthreadCall("destroy cv", pthread_cond_destroy(&cv_));+}++void CondVar::Wait() {+ PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_));+}++void CondVar::Signal(){+ PthreadCall("signal", pthread_cond_signal(&cv_));+}++void CondVar::SignalAll() {+ PthreadCall("broadcast", pthread_cond_broadcast(&cv_));+}++} // namespace port+} // namespace leveldb
+ cbits/leveldb/port/port_android.h view
@@ -0,0 +1,156 @@+// 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.+//+// See port_example.h for documentation for the following types/functions.++#ifndef STORAGE_LEVELDB_PORT_PORT_ANDROID_H_+#define STORAGE_LEVELDB_PORT_PORT_ANDROID_H_++#include <endian.h>+#include <pthread.h>+#include <stdint.h>+#include <cstdatomic>+#include <string>+#include <cctype>++// Collapse the plethora of ARM flavors available to an easier to manage set+// Defs reference is at https://wiki.edubuntu.org/ARM/Thumb2PortingHowto+#if defined(__ARM_ARCH_6__) || \+ defined(__ARM_ARCH_6J__) || \+ defined(__ARM_ARCH_6K__) || \+ defined(__ARM_ARCH_6Z__) || \+ defined(__ARM_ARCH_6T2__) || \+ defined(__ARM_ARCH_6ZK__) || \+ defined(__ARM_ARCH_7__) || \+ defined(__ARM_ARCH_7R__) || \+ defined(__ARM_ARCH_7A__)+#define ARMV6_OR_7 1+#endif++extern "C" {+ size_t fread_unlocked(void *a, size_t b, size_t c, FILE *d);+ size_t fwrite_unlocked(const void *a, size_t b, size_t c, FILE *d);+ int fflush_unlocked(FILE *f);+ int fdatasync (int fd);+}++namespace leveldb {+namespace port {++static const bool kLittleEndian = __BYTE_ORDER == __LITTLE_ENDIAN;++class CondVar;++class Mutex {+ public:+ Mutex();+ ~Mutex();++ void Lock();+ void Unlock();+ void AssertHeld() {+ //TODO(gabor): How can I implement this?+ }++ private:+ friend class CondVar;+ pthread_mutex_t mu_;++ // No copying+ Mutex(const Mutex&);+ void operator=(const Mutex&);+};++class CondVar {+ public:+ explicit CondVar(Mutex* mu);+ ~CondVar();+ void Wait();+ void Signal();+ void SignalAll();+ private:+ Mutex* mu_;+ pthread_cond_t cv_;+};++#ifndef ARMV6_OR_7+// On ARM chipsets <V6, 0xffff0fa0 is the hard coded address of a +// memory barrier function provided by the kernel.+typedef void (*LinuxKernelMemoryBarrierFunc)(void);+LinuxKernelMemoryBarrierFunc pLinuxKernelMemoryBarrier ATTRIBUTE_WEAK =+ (LinuxKernelMemoryBarrierFunc) 0xffff0fa0;+#endif++// Storage for a lock-free pointer+class AtomicPointer {+ private:+ void* rep_;++ inline void MemoryBarrier() const {+ // TODO(gabor): This only works on Android instruction sets >= V6+#ifdef ARMV6_OR_7+ __asm__ __volatile__("dmb" : : : "memory");+#else+ pLinuxKernelMemoryBarrier();+#endif+ }++ public:+ AtomicPointer() { }+ explicit AtomicPointer(void* v) : rep_(v) { }+ inline void* Acquire_Load() const {+ void* r = rep_;+ MemoryBarrier();+ return r;+ }+ inline void Release_Store(void* v) {+ MemoryBarrier();+ rep_ = v;+ }+ inline void* NoBarrier_Load() const {+ void* r = rep_;+ return r;+ }+ inline void NoBarrier_Store(void* v) {+ rep_ = v;+ }+};++// TODO(gabor): Implement compress+inline bool Snappy_Compress(+ const char* input,+ size_t input_length,+ std::string* output) {+ return false;+}++// TODO(gabor): Implement uncompress+inline bool Snappy_GetUncompressedLength(const char* input, size_t length,+ size_t* result) {+ return false;+}++// TODO(gabor): Implement uncompress+inline bool Snappy_Uncompress(+ const char* input_data,+ size_t input_length,+ char* output) {+ return false;+}++inline uint64_t ThreadIdentifier() {+ pthread_t tid = pthread_self();+ uint64_t r = 0;+ memcpy(&r, &tid, sizeof(r) < sizeof(tid) ? sizeof(r) : sizeof(tid));+ return r;+}++inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) {+ return false;+}++} // namespace port+} // namespace leveldb++#endif // STORAGE_LEVELDB_PORT_PORT_ANDROID_H_
+ cbits/leveldb/port/port_example.h view
@@ -0,0 +1,125 @@+// 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.+//+// This file contains the specification, but not the implementations,+// of the types/operations/etc. that should be defined by a platform+// specific port_<platform>.h file. Use this file as a reference for+// how to port this package to a new platform.++#ifndef STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_+#define STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_++namespace leveldb {+namespace port {++// TODO(jorlow): Many of these belong more in the environment class rather than+// here. We should try moving them and see if it affects perf.++// The following boolean constant must be true on a little-endian machine+// and false otherwise.+static const bool kLittleEndian = true /* or some other expression */;++// ------------------ Threading -------------------++// A Mutex represents an exclusive lock.+class Mutex {+ public:+ Mutex();+ ~Mutex();++ // Lock the mutex. Waits until other lockers have exited.+ // Will deadlock if the mutex is already locked by this thread.+ void Lock();++ // Unlock the mutex.+ // REQUIRES: This mutex was locked by this thread.+ void Unlock();++ // Optionally crash if this thread does not hold this mutex.+ // The implementation must be fast, especially if NDEBUG is+ // defined. The implementation is allowed to skip all checks.+ void AssertHeld();+};++class CondVar {+ public:+ explicit CondVar(Mutex* mu);+ ~CondVar();++ // Atomically release *mu and block on this condition variable until+ // either a call to SignalAll(), or a call to Signal() that picks+ // this thread to wakeup.+ // REQUIRES: this thread holds *mu+ void Wait();++ // If there are some threads waiting, wake up at least one of them.+ void Signal();++ // Wake up all waiting threads.+ void SignallAll();+};++// A type that holds a pointer that can be read or written atomically+// (i.e., without word-tearing.)+class AtomicPointer {+ private:+ intptr_t rep_;+ public:+ // Initialize to arbitrary value+ AtomicPointer();++ // Initialize to hold v+ explicit AtomicPointer(void* v) : rep_(v) { }++ // Read and return the stored pointer with the guarantee that no+ // later memory access (read or write) by this thread can be+ // reordered ahead of this read.+ void* Acquire_Load() const;++ // Set v as the stored pointer with the guarantee that no earlier+ // memory access (read or write) by this thread can be reordered+ // after this store.+ void Release_Store(void* v);++ // Read the stored pointer with no ordering guarantees.+ void* NoBarrier_Load() const;++ // Set va as the stored pointer with no ordering guarantees.+ void NoBarrier_Store(void* v);+};++// ------------------ Compression -------------------++// Store the snappy compression of "input[0,input_length-1]" in *output.+// Returns false if snappy is not supported by this port.+extern bool Snappy_Compress(const char* input, size_t input_length,+ std::string* output);++// If input[0,input_length-1] looks like a valid snappy compressed+// buffer, store the size of the uncompressed data in *result and+// return true. Else return false.+extern bool Snappy_GetUncompressedLength(const char* input, size_t length,+ size_t* result);++// Attempt to snappy uncompress input[0,input_length-1] into *output.+// Returns true if successful, false if the input is invalid lightweight+// compressed data.+//+// REQUIRES: at least the first "n" bytes of output[] must be writable+// where "n" is the result of a successful call to+// Snappy_GetUncompressedLength.+extern bool Snappy_Uncompress(const char* input_data, size_t input_length,+ char* output);++// ------------------ Miscellaneous -------------------++// If heap profiling is not supported, returns false.+// Else repeatedly calls (*func)(arg, data, n) and then returns true.+// The concatenation of all "data[0,n-1]" fragments is the heap profile.+extern bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg);++} // namespace port+} // namespace leveldb++#endif // STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_
+ cbits/leveldb/port/port_posix.cc view
@@ -0,0 +1,50 @@+// 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 "port/port_posix.h"++#include <cstdlib>+#include <stdio.h>+#include <string.h>+#include "util/logging.h"++namespace leveldb {+namespace port {++static void PthreadCall(const char* label, int result) {+ if (result != 0) {+ fprintf(stderr, "pthread %s: %s\n", label, strerror(result));+ abort();+ }+}++Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); }++Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); }++void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); }++void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); }++CondVar::CondVar(Mutex* mu)+ : mu_(mu) {+ PthreadCall("init cv", pthread_cond_init(&cv_, NULL));+}++CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); }++void CondVar::Wait() {+ PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_));+}++void CondVar::Signal() {+ PthreadCall("signal", pthread_cond_signal(&cv_));+}++void CondVar::SignalAll() {+ PthreadCall("broadcast", pthread_cond_broadcast(&cv_));+}++} // namespace port+} // namespace leveldb
+ cbits/leveldb/port/port_posix.h view
@@ -0,0 +1,121 @@+// 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.+//+// See port_example.h for documentation for the following types/functions.++#ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_+#define STORAGE_LEVELDB_PORT_PORT_POSIX_H_++#if defined(OS_MACOSX) || defined(OS_FREEBSD)+ #include <machine/endian.h>+#elif defined(OS_SOLARIS)+ #include <sys/isa_defs.h>+ #ifdef _LITTLE_ENDIAN+ #define LITTLE_ENDIAN+ #else+ #define BIG_ENDIAN+ #endif+#else+ #include <endian.h>+#endif+#include <pthread.h>+#ifdef SNAPPY+#include <snappy.h>+#endif+#include <stdint.h>+#include <string>+#include "port/atomic_pointer.h"++#ifdef LITTLE_ENDIAN+#define IS_LITTLE_ENDIAN true+#else+#define IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN)+#endif++#if defined(OS_MACOSX) || defined(OS_SOLARIS) || defined(OS_FREEBSD)+#define fread_unlocked fread+#define fwrite_unlocked fwrite+#define fflush_unlocked fflush+#endif++#if defined(OS_MACOSX) || defined(OS_FREEBSD)+#define fdatasync fsync+#endif++namespace leveldb {+namespace port {++static const bool kLittleEndian = IS_LITTLE_ENDIAN;++class CondVar;++class Mutex {+ public:+ Mutex();+ ~Mutex();++ void Lock();+ void Unlock();+ void AssertHeld() { }++ private:+ friend class CondVar;+ pthread_mutex_t mu_;++ // No copying+ Mutex(const Mutex&);+ void operator=(const Mutex&);+};++class CondVar {+ public:+ explicit CondVar(Mutex* mu);+ ~CondVar();+ void Wait();+ void Signal();+ void SignalAll();+ private:+ pthread_cond_t cv_;+ Mutex* mu_;+};++inline bool Snappy_Compress(const char* input, size_t length,+ ::std::string* output) {+#ifdef SNAPPY+ output->resize(snappy::MaxCompressedLength(length));+ size_t outlen;+ snappy::RawCompress(input, length, &(*output)[0], &outlen);+ output->resize(outlen);+ return true;+#endif++ return false;+}++inline bool Snappy_GetUncompressedLength(const char* input, size_t length,+ size_t* result) {+#ifdef SNAPPY+ return snappy::GetUncompressedLength(input, length, result);+#else+ return false;+#endif+}++inline bool Snappy_Uncompress(const char* input, size_t length,+ char* output) {+#ifdef SNAPPY+ return snappy::RawUncompress(input, length, output);+#else+ return false;+#endif+}++inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) {+ return false;+}++} // namespace port+} // namespace leveldb++#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_
+ cbits/leveldb/table/block.cc view
@@ -0,0 +1,263 @@+// 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.+//+// Decodes the blocks generated by block_builder.cc.++#include "table/block.h"++#include <vector>+#include <algorithm>+#include "leveldb/comparator.h"+#include "util/coding.h"+#include "util/logging.h"++namespace leveldb {++inline uint32_t Block::NumRestarts() const {+ assert(size_ >= 2*sizeof(uint32_t));+ return DecodeFixed32(data_ + size_ - sizeof(uint32_t));+}++Block::Block(const char* data, size_t size)+ : data_(data),+ size_(size) {+ if (size_ < sizeof(uint32_t)) {+ size_ = 0; // Error marker+ } else {+ restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t);+ if (restart_offset_ > size_ - sizeof(uint32_t)) {+ // The size is too small for NumRestarts() and therefore+ // restart_offset_ wrapped around.+ size_ = 0;+ }+ }+}++Block::~Block() {+ delete[] data_;+}++// Helper routine: decode the next block entry starting at "p",+// storing the number of shared key bytes, non_shared key bytes,+// and the length of the value in "*shared", "*non_shared", and+// "*value_length", respectively. Will not derefence past "limit".+//+// If any errors are detected, returns NULL. Otherwise, returns a+// pointer to the key delta (just past the three decoded values).+static inline const char* DecodeEntry(const char* p, const char* limit,+ uint32_t* shared,+ uint32_t* non_shared,+ uint32_t* value_length) {+ if (limit - p < 3) return NULL;+ *shared = reinterpret_cast<const unsigned char*>(p)[0];+ *non_shared = reinterpret_cast<const unsigned char*>(p)[1];+ *value_length = reinterpret_cast<const unsigned char*>(p)[2];+ if ((*shared | *non_shared | *value_length) < 128) {+ // Fast path: all three values are encoded in one byte each+ p += 3;+ } else {+ if ((p = GetVarint32Ptr(p, limit, shared)) == NULL) return NULL;+ if ((p = GetVarint32Ptr(p, limit, non_shared)) == NULL) return NULL;+ if ((p = GetVarint32Ptr(p, limit, value_length)) == NULL) return NULL;+ }++ if (static_cast<uint32_t>(limit - p) < (*non_shared + *value_length)) {+ return NULL;+ }+ return p;+}++class Block::Iter : public Iterator {+ private:+ const Comparator* const comparator_;+ const char* const data_; // underlying block contents+ uint32_t const restarts_; // Offset of restart array (list of fixed32)+ uint32_t const num_restarts_; // Number of uint32_t entries in restart array++ // current_ is offset in data_ of current entry. >= restarts_ if !Valid+ uint32_t current_;+ uint32_t restart_index_; // Index of restart block in which current_ falls+ std::string key_;+ Slice value_;+ Status status_;++ inline int Compare(const Slice& a, const Slice& b) const {+ return comparator_->Compare(a, b);+ }++ // Return the offset in data_ just past the end of the current entry.+ inline uint32_t NextEntryOffset() const {+ return (value_.data() + value_.size()) - data_;+ }++ uint32_t GetRestartPoint(uint32_t index) {+ assert(index < num_restarts_);+ return DecodeFixed32(data_ + restarts_ + index * sizeof(uint32_t));+ }++ void SeekToRestartPoint(uint32_t index) {+ key_.clear();+ restart_index_ = index;+ // current_ will be fixed by ParseNextKey();++ // ParseNextKey() starts at the end of value_, so set value_ accordingly+ uint32_t offset = GetRestartPoint(index);+ value_ = Slice(data_ + offset, 0);+ }++ public:+ Iter(const Comparator* comparator,+ const char* data,+ uint32_t restarts,+ uint32_t num_restarts)+ : comparator_(comparator),+ data_(data),+ restarts_(restarts),+ num_restarts_(num_restarts),+ current_(restarts_),+ restart_index_(num_restarts_) {+ assert(num_restarts_ > 0);+ }++ virtual bool Valid() const { return current_ < restarts_; }+ virtual Status status() const { return status_; }+ virtual Slice key() const {+ assert(Valid());+ return key_;+ }+ virtual Slice value() const {+ assert(Valid());+ return value_;+ }++ virtual void Next() {+ assert(Valid());+ ParseNextKey();+ }++ virtual void Prev() {+ assert(Valid());++ // Scan backwards to a restart point before current_+ const uint32_t original = current_;+ while (GetRestartPoint(restart_index_) >= original) {+ if (restart_index_ == 0) {+ // No more entries+ current_ = restarts_;+ restart_index_ = num_restarts_;+ return;+ }+ restart_index_--;+ }++ SeekToRestartPoint(restart_index_);+ do {+ // Loop until end of current entry hits the start of original entry+ } while (ParseNextKey() && NextEntryOffset() < original);+ }++ virtual void Seek(const Slice& target) {+ // Binary search in restart array to find the first restart point+ // with a key >= target+ uint32_t left = 0;+ uint32_t right = num_restarts_ - 1;+ while (left < right) {+ uint32_t mid = (left + right + 1) / 2;+ uint32_t region_offset = GetRestartPoint(mid);+ uint32_t shared, non_shared, value_length;+ const char* key_ptr = DecodeEntry(data_ + region_offset,+ data_ + restarts_,+ &shared, &non_shared, &value_length);+ if (key_ptr == NULL || (shared != 0)) {+ CorruptionError();+ return;+ }+ Slice mid_key(key_ptr, non_shared);+ if (Compare(mid_key, target) < 0) {+ // Key at "mid" is smaller than "target". Therefore all+ // blocks before "mid" are uninteresting.+ left = mid;+ } else {+ // Key at "mid" is >= "target". Therefore all blocks at or+ // after "mid" are uninteresting.+ right = mid - 1;+ }+ }++ // Linear search (within restart block) for first key >= target+ SeekToRestartPoint(left);+ while (true) {+ if (!ParseNextKey()) {+ return;+ }+ if (Compare(key_, target) >= 0) {+ return;+ }+ }+ }++ virtual void SeekToFirst() {+ SeekToRestartPoint(0);+ ParseNextKey();+ }++ virtual void SeekToLast() {+ SeekToRestartPoint(num_restarts_ - 1);+ while (ParseNextKey() && NextEntryOffset() < restarts_) {+ // Keep skipping+ }+ }++ private:+ void CorruptionError() {+ current_ = restarts_;+ restart_index_ = num_restarts_;+ status_ = Status::Corruption("bad entry in block");+ key_.clear();+ value_.clear();+ }++ bool ParseNextKey() {+ current_ = NextEntryOffset();+ const char* p = data_ + current_;+ const char* limit = data_ + restarts_; // Restarts come right after data+ if (p >= limit) {+ // No more entries to return. Mark as invalid.+ current_ = restarts_;+ restart_index_ = num_restarts_;+ return false;+ }++ // Decode next entry+ uint32_t shared, non_shared, value_length;+ p = DecodeEntry(p, limit, &shared, &non_shared, &value_length);+ if (p == NULL || key_.size() < shared) {+ CorruptionError();+ return false;+ } else {+ key_.resize(shared);+ key_.append(p, non_shared);+ value_ = Slice(p + non_shared, value_length);+ while (restart_index_ + 1 < num_restarts_ &&+ GetRestartPoint(restart_index_ + 1) < current_) {+ ++restart_index_;+ }+ return true;+ }+ }+};++Iterator* Block::NewIterator(const Comparator* cmp) {+ if (size_ < 2*sizeof(uint32_t)) {+ return NewErrorIterator(Status::Corruption("bad block contents"));+ }+ const uint32_t num_restarts = NumRestarts();+ if (num_restarts == 0) {+ return NewEmptyIterator();+ } else {+ return new Iter(cmp, data_, restart_offset_, num_restarts);+ }+}++} // namespace leveldb
+ cbits/leveldb/table/block.h view
@@ -0,0 +1,43 @@+// 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.++#ifndef STORAGE_LEVELDB_TABLE_BLOCK_H_+#define STORAGE_LEVELDB_TABLE_BLOCK_H_++#include <stddef.h>+#include <stdint.h>+#include "leveldb/iterator.h"++namespace leveldb {++class Comparator;++class Block {+ public:+ // Initialize the block with the specified contents.+ // Takes ownership of data[] and will delete[] it when done.+ Block(const char* data, size_t size);++ ~Block();++ size_t size() const { return size_; }+ Iterator* NewIterator(const Comparator* comparator);++ private:+ uint32_t NumRestarts() const;++ const char* data_;+ size_t size_;+ uint32_t restart_offset_; // Offset in data_ of restart array++ // No copying allowed+ Block(const Block&);+ void operator=(const Block&);++ class Iter;+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_TABLE_BLOCK_H_
+ cbits/leveldb/table/block_builder.cc view
@@ -0,0 +1,109 @@+// 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.+//+// BlockBuilder generates blocks where keys are prefix-compressed:+//+// When we store a key, we drop the prefix shared with the previous+// string. This helps reduce the space requirement significantly.+// Furthermore, once every K keys, we do not apply the prefix+// compression and store the entire key. We call this a "restart+// point". The tail end of the block stores the offsets of all of the+// restart points, and can be used to do a binary search when looking+// for a particular key. Values are stored as-is (without compression)+// immediately following the corresponding key.+//+// An entry for a particular key-value pair has the form:+// shared_bytes: varint32+// unshared_bytes: varint32+// value_length: varint32+// key_delta: char[unshared_bytes]+// value: char[value_length]+// shared_bytes == 0 for restart points.+//+// The trailer of the block has the form:+// restarts: uint32[num_restarts]+// num_restarts: uint32+// restarts[i] contains the offset within the block of the ith restart point.++#include "table/block_builder.h"++#include <algorithm>+#include <assert.h>+#include "leveldb/comparator.h"+#include "leveldb/table_builder.h"+#include "util/coding.h"++namespace leveldb {++BlockBuilder::BlockBuilder(const Options* options)+ : options_(options),+ restarts_(),+ counter_(0),+ finished_(false) {+ assert(options->block_restart_interval >= 1);+ restarts_.push_back(0); // First restart point is at offset 0+}++void BlockBuilder::Reset() {+ buffer_.clear();+ restarts_.clear();+ restarts_.push_back(0); // First restart point is at offset 0+ counter_ = 0;+ finished_ = false;+ last_key_.clear();+}++size_t BlockBuilder::CurrentSizeEstimate() const {+ return (buffer_.size() + // Raw data buffer+ restarts_.size() * sizeof(uint32_t) + // Restart array+ sizeof(uint32_t)); // Restart array length+}++Slice BlockBuilder::Finish() {+ // Append restart array+ for (size_t i = 0; i < restarts_.size(); i++) {+ PutFixed32(&buffer_, restarts_[i]);+ }+ PutFixed32(&buffer_, restarts_.size());+ finished_ = true;+ return Slice(buffer_);+}++void BlockBuilder::Add(const Slice& key, const Slice& value) {+ Slice last_key_piece(last_key_);+ assert(!finished_);+ assert(counter_ <= options_->block_restart_interval);+ assert(buffer_.empty() // No values yet?+ || options_->comparator->Compare(key, last_key_piece) > 0);+ size_t shared = 0;+ if (counter_ < options_->block_restart_interval) {+ // See how much sharing to do with previous string+ const size_t min_length = std::min(last_key_piece.size(), key.size());+ while ((shared < min_length) && (last_key_piece[shared] == key[shared])) {+ shared++;+ }+ } else {+ // Restart compression+ restarts_.push_back(buffer_.size());+ counter_ = 0;+ }+ const size_t non_shared = key.size() - shared;++ // Add "<shared><non_shared><value_size>" to buffer_+ PutVarint32(&buffer_, shared);+ PutVarint32(&buffer_, non_shared);+ PutVarint32(&buffer_, value.size());++ // Add string delta to buffer_ followed by value+ buffer_.append(key.data() + shared, non_shared);+ buffer_.append(value.data(), value.size());++ // Update state+ last_key_.resize(shared);+ last_key_.append(key.data() + shared, non_shared);+ assert(Slice(last_key_) == key);+ counter_++;+}++} // namespace leveldb
+ cbits/leveldb/table/block_builder.h view
@@ -0,0 +1,57 @@+// 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.++#ifndef STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_+#define STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_++#include <vector>++#include <stdint.h>+#include "leveldb/slice.h"++namespace leveldb {++struct Options;++class BlockBuilder {+ public:+ explicit BlockBuilder(const Options* options);++ // Reset the contents as if the BlockBuilder was just constructed.+ void Reset();++ // REQUIRES: Finish() has not been callled since the last call to Reset().+ // REQUIRES: key is larger than any previously added key+ void Add(const Slice& key, const Slice& value);++ // Finish building the block and return a slice that refers to the+ // block contents. The returned slice will remain valid for the+ // lifetime of this builder or until Reset() is called.+ Slice Finish();++ // Returns an estimate of the current (uncompressed) size of the block+ // we are building.+ size_t CurrentSizeEstimate() const;++ // Return true iff no entries have been added since the last Reset()+ bool empty() const {+ return buffer_.empty();+ }++ private:+ const Options* options_;+ std::string buffer_; // Destination buffer+ std::vector<uint32_t> restarts_; // Restart points+ int counter_; // Number of entries emitted since restart+ bool finished_; // Has Finish() been called?+ std::string last_key_;++ // No copying allowed+ BlockBuilder(const BlockBuilder&);+ void operator=(const BlockBuilder&);+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_
+ cbits/leveldb/table/format.cc view
@@ -0,0 +1,135 @@+// 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 "table/format.h"++#include "leveldb/env.h"+#include "port/port.h"+#include "table/block.h"+#include "util/coding.h"+#include "util/crc32c.h"++namespace leveldb {++void BlockHandle::EncodeTo(std::string* dst) const {+ // Sanity check that all fields have been set+ assert(offset_ != ~static_cast<uint64_t>(0));+ assert(size_ != ~static_cast<uint64_t>(0));+ PutVarint64(dst, offset_);+ PutVarint64(dst, size_);+}++Status BlockHandle::DecodeFrom(Slice* input) {+ if (GetVarint64(input, &offset_) &&+ GetVarint64(input, &size_)) {+ return Status::OK();+ } else {+ return Status::Corruption("bad block handle");+ }+}++void Footer::EncodeTo(std::string* dst) const {+#ifndef NDEBUG+ const size_t original_size = dst->size();+#endif+ metaindex_handle_.EncodeTo(dst);+ index_handle_.EncodeTo(dst);+ dst->resize(2 * BlockHandle::kMaxEncodedLength); // Padding+ PutFixed32(dst, static_cast<uint32_t>(kTableMagicNumber & 0xffffffffu));+ PutFixed32(dst, static_cast<uint32_t>(kTableMagicNumber >> 32));+ assert(dst->size() == original_size + kEncodedLength);+}++Status Footer::DecodeFrom(Slice* input) {+ const char* magic_ptr = input->data() + kEncodedLength - 8;+ const uint32_t magic_lo = DecodeFixed32(magic_ptr);+ const uint32_t magic_hi = DecodeFixed32(magic_ptr + 4);+ const uint64_t magic = ((static_cast<uint64_t>(magic_hi) << 32) |+ (static_cast<uint64_t>(magic_lo)));+ if (magic != kTableMagicNumber) {+ return Status::InvalidArgument("not an sstable (bad magic number)");+ }++ Status result = metaindex_handle_.DecodeFrom(input);+ if (result.ok()) {+ result = index_handle_.DecodeFrom(input);+ }+ if (result.ok()) {+ // We skip over any leftover data (just padding for now) in "input"+ const char* end = magic_ptr + 8;+ *input = Slice(end, input->data() + input->size() - end);+ }+ return result;+}++Status ReadBlock(RandomAccessFile* file,+ const ReadOptions& options,+ const BlockHandle& handle,+ Block** block) {+ *block = NULL;++ // Read the block contents as well as the type/crc footer.+ // See table_builder.cc for the code that built this structure.+ size_t n = static_cast<size_t>(handle.size());+ char* buf = new char[n + kBlockTrailerSize];+ Slice contents;+ Status s = file->Read(handle.offset(), n + kBlockTrailerSize, &contents, buf);+ if (!s.ok()) {+ delete[] buf;+ return s;+ }+ if (contents.size() != n + kBlockTrailerSize) {+ delete[] buf;+ return Status::Corruption("truncated block read");+ }++ // Check the crc of the type and the block contents+ const char* data = contents.data(); // Pointer to where Read put the data+ if (options.verify_checksums) {+ const uint32_t crc = crc32c::Unmask(DecodeFixed32(data + n + 1));+ const uint32_t actual = crc32c::Value(data, n + 1);+ if (actual != crc) {+ delete[] buf;+ s = Status::Corruption("block checksum mismatch");+ return s;+ }+ }++ switch (data[n]) {+ case kNoCompression:+ if (data != buf) {+ // File implementation gave us pointer to some other data.+ // Copy into buf[].+ memcpy(buf, data, n + kBlockTrailerSize);+ }++ // Ok+ break;+ case kSnappyCompression: {+ size_t ulength = 0;+ if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) {+ delete[] buf;+ return Status::Corruption("corrupted compressed block contents");+ }+ char* ubuf = new char[ulength];+ if (!port::Snappy_Uncompress(data, n, ubuf)) {+ delete[] buf;+ delete[] ubuf;+ return Status::Corruption("corrupted compressed block contents");+ }+ delete[] buf;+ buf = ubuf;+ n = ulength;+ break;+ }+ default:+ delete[] buf;+ return Status::Corruption("bad block type");+ }++ *block = new Block(buf, n); // Block takes ownership of buf[]+ return Status::OK();+}++} // namespace leveldb
+ cbits/leveldb/table/format.h view
@@ -0,0 +1,103 @@+// 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.++#ifndef STORAGE_LEVELDB_TABLE_FORMAT_H_+#define STORAGE_LEVELDB_TABLE_FORMAT_H_++#include <string>+#include <stdint.h>+#include "leveldb/slice.h"+#include "leveldb/status.h"+#include "leveldb/table_builder.h"++namespace leveldb {++class Block;+class RandomAccessFile;+struct ReadOptions;++// BlockHandle is a pointer to the extent of a file that stores a data+// block or a meta block.+class BlockHandle {+ public:+ BlockHandle();++ // The offset of the block in the file.+ uint64_t offset() const { return offset_; }+ void set_offset(uint64_t offset) { offset_ = offset; }++ // The size of the stored block+ uint64_t size() const { return size_; }+ void set_size(uint64_t size) { size_ = size; }++ void EncodeTo(std::string* dst) const;+ Status DecodeFrom(Slice* input);++ // Maximum encoding length of a BlockHandle+ enum { kMaxEncodedLength = 10 + 10 };++ private:+ uint64_t offset_;+ uint64_t size_;+};++// Footer encapsulates the fixed information stored at the tail+// end of every table file.+class Footer {+ public:+ Footer() { }++ // The block handle for the metaindex block of the table+ const BlockHandle& metaindex_handle() const { return metaindex_handle_; }+ void set_metaindex_handle(const BlockHandle& h) { metaindex_handle_ = h; }++ // The block handle for the index block of the table+ const BlockHandle& index_handle() const {+ return index_handle_;+ }+ void set_index_handle(const BlockHandle& h) {+ index_handle_ = h;+ }++ void EncodeTo(std::string* dst) const;+ Status DecodeFrom(Slice* input);++ // Encoded length of a Footer. Note that the serialization of a+ // Footer will always occupy exactly this many bytes. It consists+ // of two block handles and a magic number.+ enum {+ kEncodedLength = 2*BlockHandle::kMaxEncodedLength + 8+ };++ private:+ BlockHandle metaindex_handle_;+ BlockHandle index_handle_;+};++// kTableMagicNumber was picked by running+// echo http://code.google.com/p/leveldb/ | sha1sum+// and taking the leading 64 bits.+static const uint64_t kTableMagicNumber = 0xdb4775248b80fb57ull;++// 1-byte type + 32-bit crc+static const size_t kBlockTrailerSize = 5;++// Read the block identified by "handle" from "file". On success,+// store a pointer to the heap-allocated result in *block and return+// OK. On failure store NULL in *block and return non-OK.+extern Status ReadBlock(RandomAccessFile* file,+ const ReadOptions& options,+ const BlockHandle& handle,+ Block** block);++// Implementation details follow. Clients should ignore,++inline BlockHandle::BlockHandle()+ : offset_(~static_cast<uint64_t>(0)),+ size_(~static_cast<uint64_t>(0)) {+}++} // namespace leveldb++#endif // STORAGE_LEVELDB_TABLE_FORMAT_H_
+ cbits/leveldb/table/iterator.cc view
@@ -0,0 +1,67 @@+// 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/iterator.h"++namespace leveldb {++Iterator::Iterator() {+ cleanup_.function = NULL;+ cleanup_.next = NULL;+}++Iterator::~Iterator() {+ if (cleanup_.function != NULL) {+ (*cleanup_.function)(cleanup_.arg1, cleanup_.arg2);+ for (Cleanup* c = cleanup_.next; c != NULL; ) {+ (*c->function)(c->arg1, c->arg2);+ Cleanup* next = c->next;+ delete c;+ c = next;+ }+ }+}++void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) {+ assert(func != NULL);+ Cleanup* c;+ if (cleanup_.function == NULL) {+ c = &cleanup_;+ } else {+ c = new Cleanup;+ c->next = cleanup_.next;+ cleanup_.next = c;+ }+ c->function = func;+ c->arg1 = arg1;+ c->arg2 = arg2;+}++namespace {+class EmptyIterator : public Iterator {+ public:+ EmptyIterator(const Status& s) : status_(s) { }+ virtual bool Valid() const { return false; }+ virtual void Seek(const Slice& target) { }+ virtual void SeekToFirst() { }+ virtual void SeekToLast() { }+ virtual void Next() { assert(false); }+ virtual void Prev() { assert(false); }+ Slice key() const { assert(false); return Slice(); }+ Slice value() const { assert(false); return Slice(); }+ virtual Status status() const { return status_; }+ private:+ Status status_;+};+} // namespace++Iterator* NewEmptyIterator() {+ return new EmptyIterator(Status::OK());+}++Iterator* NewErrorIterator(const Status& status) {+ return new EmptyIterator(status);+}++} // namespace leveldb
+ cbits/leveldb/table/iterator_wrapper.h view
@@ -0,0 +1,63 @@+// 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.++#ifndef STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_+#define STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_++namespace leveldb {++// A internal wrapper class with an interface similar to Iterator that+// caches the valid() and key() results for an underlying iterator.+// This can help avoid virtual function calls and also gives better+// cache locality.+class IteratorWrapper {+ public:+ IteratorWrapper(): iter_(NULL), valid_(false) { }+ explicit IteratorWrapper(Iterator* iter): iter_(NULL) {+ Set(iter);+ }+ ~IteratorWrapper() { delete iter_; }+ Iterator* iter() const { return iter_; }++ // Takes ownership of "iter" and will delete it when destroyed, or+ // when Set() is invoked again.+ void Set(Iterator* iter) {+ delete iter_;+ iter_ = iter;+ if (iter_ == NULL) {+ valid_ = false;+ } else {+ Update();+ }+ }+++ // Iterator interface methods+ bool Valid() const { return valid_; }+ Slice key() const { assert(Valid()); return key_; }+ Slice value() const { assert(Valid()); return iter_->value(); }+ // Methods below require iter() != NULL+ Status status() const { assert(iter_); return iter_->status(); }+ void Next() { assert(iter_); iter_->Next(); Update(); }+ void Prev() { assert(iter_); iter_->Prev(); Update(); }+ void Seek(const Slice& k) { assert(iter_); iter_->Seek(k); Update(); }+ void SeekToFirst() { assert(iter_); iter_->SeekToFirst(); Update(); }+ void SeekToLast() { assert(iter_); iter_->SeekToLast(); Update(); }++ private:+ void Update() {+ valid_ = iter_->Valid();+ if (valid_) {+ key_ = iter_->key();+ }+ }++ Iterator* iter_;+ bool valid_;+ Slice key_;+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_TABLE_ITERATOR_WRAPPER_H_
+ cbits/leveldb/table/merger.cc view
@@ -0,0 +1,197 @@+// 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 "table/merger.h"++#include "leveldb/comparator.h"+#include "leveldb/iterator.h"+#include "table/iterator_wrapper.h"++namespace leveldb {++namespace {+class MergingIterator : public Iterator {+ public:+ MergingIterator(const Comparator* comparator, Iterator** children, int n)+ : comparator_(comparator),+ children_(new IteratorWrapper[n]),+ n_(n),+ current_(NULL),+ direction_(kForward) {+ for (int i = 0; i < n; i++) {+ children_[i].Set(children[i]);+ }+ }++ virtual ~MergingIterator() {+ delete[] children_;+ }++ virtual bool Valid() const {+ return (current_ != NULL);+ }++ virtual void SeekToFirst() {+ for (int i = 0; i < n_; i++) {+ children_[i].SeekToFirst();+ }+ FindSmallest();+ direction_ = kForward;+ }++ virtual void SeekToLast() {+ for (int i = 0; i < n_; i++) {+ children_[i].SeekToLast();+ }+ FindLargest();+ direction_ = kReverse;+ }++ virtual void Seek(const Slice& target) {+ for (int i = 0; i < n_; i++) {+ children_[i].Seek(target);+ }+ FindSmallest();+ direction_ = kForward;+ }++ virtual void Next() {+ assert(Valid());++ // Ensure that all children are positioned after key().+ // If we are moving in the forward direction, it is already+ // true for all of the non-current_ children since current_ is+ // the smallest child and key() == current_->key(). Otherwise,+ // we explicitly position the non-current_ children.+ if (direction_ != kForward) {+ for (int i = 0; i < n_; i++) {+ IteratorWrapper* child = &children_[i];+ if (child != current_) {+ child->Seek(key());+ if (child->Valid() &&+ comparator_->Compare(key(), child->key()) == 0) {+ child->Next();+ }+ }+ }+ direction_ = kForward;+ }++ current_->Next();+ FindSmallest();+ }++ virtual void Prev() {+ assert(Valid());++ // Ensure that all children are positioned before key().+ // If we are moving in the reverse direction, it is already+ // true for all of the non-current_ children since current_ is+ // the largest child and key() == current_->key(). Otherwise,+ // we explicitly position the non-current_ children.+ if (direction_ != kReverse) {+ for (int i = 0; i < n_; i++) {+ IteratorWrapper* child = &children_[i];+ if (child != current_) {+ child->Seek(key());+ if (child->Valid()) {+ // Child is at first entry >= key(). Step back one to be < key()+ child->Prev();+ } else {+ // Child has no entries >= key(). Position at last entry.+ child->SeekToLast();+ }+ }+ }+ direction_ = kReverse;+ }++ current_->Prev();+ FindLargest();+ }++ virtual Slice key() const {+ assert(Valid());+ return current_->key();+ }++ virtual Slice value() const {+ assert(Valid());+ return current_->value();+ }++ virtual Status status() const {+ Status status;+ for (int i = 0; i < n_; i++) {+ status = children_[i].status();+ if (!status.ok()) {+ break;+ }+ }+ return status;+ }++ private:+ void FindSmallest();+ void FindLargest();++ // We might want to use a heap in case there are lots of children.+ // For now we use a simple array since we expect a very small number+ // of children in leveldb.+ const Comparator* comparator_;+ IteratorWrapper* children_;+ int n_;+ IteratorWrapper* current_;++ // Which direction is the iterator moving?+ enum Direction {+ kForward,+ kReverse+ };+ Direction direction_;+};++void MergingIterator::FindSmallest() {+ IteratorWrapper* smallest = NULL;+ for (int i = 0; i < n_; i++) {+ IteratorWrapper* child = &children_[i];+ if (child->Valid()) {+ if (smallest == NULL) {+ smallest = child;+ } else if (comparator_->Compare(child->key(), smallest->key()) < 0) {+ smallest = child;+ }+ }+ }+ current_ = smallest;+}++void MergingIterator::FindLargest() {+ IteratorWrapper* largest = NULL;+ for (int i = n_-1; i >= 0; i--) {+ IteratorWrapper* child = &children_[i];+ if (child->Valid()) {+ if (largest == NULL) {+ largest = child;+ } else if (comparator_->Compare(child->key(), largest->key()) > 0) {+ largest = child;+ }+ }+ }+ current_ = largest;+}+} // namespace++Iterator* NewMergingIterator(const Comparator* cmp, Iterator** list, int n) {+ assert(n >= 0);+ if (n == 0) {+ return NewEmptyIterator();+ } else if (n == 1) {+ return list[0];+ } else {+ return new MergingIterator(cmp, list, n);+ }+}++} // namespace leveldb
+ cbits/leveldb/table/merger.h view
@@ -0,0 +1,26 @@+// 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.++#ifndef STORAGE_LEVELDB_TABLE_MERGER_H_+#define STORAGE_LEVELDB_TABLE_MERGER_H_++namespace leveldb {++class Comparator;+class Iterator;++// Return an iterator that provided the union of the data in+// children[0,n-1]. Takes ownership of the child iterators and+// will delete them when the result iterator is deleted.+//+// The result does no duplicate suppression. I.e., if a particular+// key is present in K child iterators, it will be yielded K times.+//+// REQUIRES: n >= 0+extern Iterator* NewMergingIterator(+ const Comparator* comparator, Iterator** children, int n);++} // namespace leveldb++#endif // STORAGE_LEVELDB_TABLE_MERGER_H_
+ cbits/leveldb/table/table.cc view
@@ -0,0 +1,175 @@+// 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/table.h"++#include "leveldb/cache.h"+#include "leveldb/env.h"+#include "table/block.h"+#include "table/format.h"+#include "table/two_level_iterator.h"+#include "util/coding.h"++namespace leveldb {++struct Table::Rep {+ ~Rep() {+ delete index_block;+ }++ Options options;+ Status status;+ RandomAccessFile* file;+ uint64_t cache_id;++ BlockHandle metaindex_handle; // Handle to metaindex_block: saved from footer+ Block* index_block;+};++Status Table::Open(const Options& options,+ RandomAccessFile* file,+ uint64_t size,+ Table** table) {+ *table = NULL;+ if (size < Footer::kEncodedLength) {+ return Status::InvalidArgument("file is too short to be an sstable");+ }++ char footer_space[Footer::kEncodedLength];+ Slice footer_input;+ Status s = file->Read(size - Footer::kEncodedLength, Footer::kEncodedLength,+ &footer_input, footer_space);+ if (!s.ok()) return s;++ Footer footer;+ s = footer.DecodeFrom(&footer_input);+ if (!s.ok()) return s;++ // Read the index block+ Block* index_block = NULL;+ if (s.ok()) {+ s = ReadBlock(file, ReadOptions(), footer.index_handle(), &index_block);+ }++ if (s.ok()) {+ // We've successfully read the footer and the index block: we're+ // ready to serve requests.+ Rep* rep = new Table::Rep;+ rep->options = options;+ rep->file = file;+ rep->metaindex_handle = footer.metaindex_handle();+ rep->index_block = index_block;+ rep->cache_id = (options.block_cache ? options.block_cache->NewId() : 0);+ *table = new Table(rep);+ } else {+ if (index_block) delete index_block;+ }++ return s;+}++Table::~Table() {+ delete rep_;+}++static void DeleteBlock(void* arg, void* ignored) {+ delete reinterpret_cast<Block*>(arg);+}++static void DeleteCachedBlock(const Slice& key, void* value) {+ Block* block = reinterpret_cast<Block*>(value);+ delete block;+}++static void ReleaseBlock(void* arg, void* h) {+ Cache* cache = reinterpret_cast<Cache*>(arg);+ Cache::Handle* handle = reinterpret_cast<Cache::Handle*>(h);+ cache->Release(handle);+}++// Convert an index iterator value (i.e., an encoded BlockHandle)+// into an iterator over the contents of the corresponding block.+Iterator* Table::BlockReader(void* arg,+ const ReadOptions& options,+ const Slice& index_value) {+ Table* table = reinterpret_cast<Table*>(arg);+ Cache* block_cache = table->rep_->options.block_cache;+ Block* block = NULL;+ Cache::Handle* cache_handle = NULL;++ BlockHandle handle;+ Slice input = index_value;+ Status s = handle.DecodeFrom(&input);+ // We intentionally allow extra stuff in index_value so that we+ // can add more features in the future.++ if (s.ok()) {+ if (block_cache != NULL) {+ char cache_key_buffer[16];+ EncodeFixed64(cache_key_buffer, table->rep_->cache_id);+ EncodeFixed64(cache_key_buffer+8, handle.offset());+ Slice key(cache_key_buffer, sizeof(cache_key_buffer));+ cache_handle = block_cache->Lookup(key);+ if (cache_handle != NULL) {+ block = reinterpret_cast<Block*>(block_cache->Value(cache_handle));+ } else {+ s = ReadBlock(table->rep_->file, options, handle, &block);+ if (s.ok() && options.fill_cache) {+ cache_handle = block_cache->Insert(+ key, block, block->size(), &DeleteCachedBlock);+ }+ }+ } else {+ s = ReadBlock(table->rep_->file, options, handle, &block);+ }+ }++ Iterator* iter;+ if (block != NULL) {+ iter = block->NewIterator(table->rep_->options.comparator);+ if (cache_handle == NULL) {+ iter->RegisterCleanup(&DeleteBlock, block, NULL);+ } else {+ iter->RegisterCleanup(&ReleaseBlock, block_cache, cache_handle);+ }+ } else {+ iter = NewErrorIterator(s);+ }+ return iter;+}++Iterator* Table::NewIterator(const ReadOptions& options) const {+ return NewTwoLevelIterator(+ rep_->index_block->NewIterator(rep_->options.comparator),+ &Table::BlockReader, const_cast<Table*>(this), options);+}++uint64_t Table::ApproximateOffsetOf(const Slice& key) const {+ Iterator* index_iter =+ rep_->index_block->NewIterator(rep_->options.comparator);+ index_iter->Seek(key);+ uint64_t result;+ if (index_iter->Valid()) {+ BlockHandle handle;+ Slice input = index_iter->value();+ Status s = handle.DecodeFrom(&input);+ if (s.ok()) {+ result = handle.offset();+ } else {+ // Strange: we can't decode the block handle in the index block.+ // We'll just return the offset of the metaindex block, which is+ // close to the whole file size for this case.+ result = rep_->metaindex_handle.offset();+ }+ } else {+ // key is past the last key in the file. Approximate the offset+ // by returning the offset of the metaindex block (which is+ // right near the end of the file).+ result = rep_->metaindex_handle.offset();+ }+ delete index_iter;+ return result;+}++} // namespace leveldb
+ cbits/leveldb/table/table_builder.cc view
@@ -0,0 +1,227 @@+// 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/table_builder.h"++#include <assert.h>+#include <stdio.h>+#include "leveldb/comparator.h"+#include "leveldb/env.h"+#include "table/block_builder.h"+#include "table/format.h"+#include "util/coding.h"+#include "util/crc32c.h"+#include "util/logging.h"++namespace leveldb {++struct TableBuilder::Rep {+ Options options;+ Options index_block_options;+ WritableFile* file;+ uint64_t offset;+ Status status;+ BlockBuilder data_block;+ BlockBuilder index_block;+ std::string last_key;+ int64_t num_entries;+ bool closed; // Either Finish() or Abandon() has been called.++ // We do not emit the index entry for a block until we have seen the+ // first key for the next data block. This allows us to use shorter+ // keys in the index block. For example, consider a block boundary+ // between the keys "the quick brown fox" and "the who". We can use+ // "the r" as the key for the index block entry since it is >= all+ // entries in the first block and < all entries in subsequent+ // blocks.+ //+ // Invariant: r->pending_index_entry is true only if data_block is empty.+ bool pending_index_entry;+ BlockHandle pending_handle; // Handle to add to index block++ std::string compressed_output;++ Rep(const Options& opt, WritableFile* f)+ : options(opt),+ index_block_options(opt),+ file(f),+ offset(0),+ data_block(&options),+ index_block(&index_block_options),+ num_entries(0),+ closed(false),+ pending_index_entry(false) {+ index_block_options.block_restart_interval = 1;+ }+};++TableBuilder::TableBuilder(const Options& options, WritableFile* file)+ : rep_(new Rep(options, file)) {+}++TableBuilder::~TableBuilder() {+ assert(rep_->closed); // Catch errors where caller forgot to call Finish()+ delete rep_;+}++Status TableBuilder::ChangeOptions(const Options& options) {+ // Note: if more fields are added to Options, update+ // this function to catch changes that should not be allowed to+ // change in the middle of building a Table.+ if (options.comparator != rep_->options.comparator) {+ return Status::InvalidArgument("changing comparator while building table");+ }++ // Note that any live BlockBuilders point to rep_->options and therefore+ // will automatically pick up the updated options.+ rep_->options = options;+ rep_->index_block_options = options;+ rep_->index_block_options.block_restart_interval = 1;+ return Status::OK();+}++void TableBuilder::Add(const Slice& key, const Slice& value) {+ Rep* r = rep_;+ assert(!r->closed);+ if (!ok()) return;+ if (r->num_entries > 0) {+ assert(r->options.comparator->Compare(key, Slice(r->last_key)) > 0);+ }++ if (r->pending_index_entry) {+ assert(r->data_block.empty());+ r->options.comparator->FindShortestSeparator(&r->last_key, key);+ std::string handle_encoding;+ r->pending_handle.EncodeTo(&handle_encoding);+ r->index_block.Add(r->last_key, Slice(handle_encoding));+ r->pending_index_entry = false;+ }++ r->last_key.assign(key.data(), key.size());+ r->num_entries++;+ r->data_block.Add(key, value);++ const size_t estimated_block_size = r->data_block.CurrentSizeEstimate();+ if (estimated_block_size >= r->options.block_size) {+ Flush();+ }+}++void TableBuilder::Flush() {+ Rep* r = rep_;+ assert(!r->closed);+ if (!ok()) return;+ if (r->data_block.empty()) return;+ assert(!r->pending_index_entry);+ WriteBlock(&r->data_block, &r->pending_handle);+ if (ok()) {+ r->pending_index_entry = true;+ r->status = r->file->Flush();+ }+}++void TableBuilder::WriteBlock(BlockBuilder* block, BlockHandle* handle) {+ // File format contains a sequence of blocks where each block has:+ // block_data: uint8[n]+ // type: uint8+ // crc: uint32+ assert(ok());+ Rep* r = rep_;+ Slice raw = block->Finish();++ Slice block_contents;+ CompressionType type = r->options.compression;+ // TODO(postrelease): Support more compression options: zlib?+ switch (type) {+ case kNoCompression:+ block_contents = raw;+ break;++ case kSnappyCompression: {+ std::string* compressed = &r->compressed_output;+ if (port::Snappy_Compress(raw.data(), raw.size(), compressed) &&+ compressed->size() < raw.size() - (raw.size() / 8u)) {+ block_contents = *compressed;+ } else {+ // Snappy not supported, or compressed less than 12.5%, so just+ // store uncompressed form+ block_contents = raw;+ type = kNoCompression;+ }+ break;+ }+ }+ handle->set_offset(r->offset);+ handle->set_size(block_contents.size());+ r->status = r->file->Append(block_contents);+ if (r->status.ok()) {+ char trailer[kBlockTrailerSize];+ trailer[0] = type;+ uint32_t crc = crc32c::Value(block_contents.data(), block_contents.size());+ crc = crc32c::Extend(crc, trailer, 1); // Extend crc to cover block type+ EncodeFixed32(trailer+1, crc32c::Mask(crc));+ r->status = r->file->Append(Slice(trailer, kBlockTrailerSize));+ if (r->status.ok()) {+ r->offset += block_contents.size() + kBlockTrailerSize;+ }+ }+ r->compressed_output.clear();+ block->Reset();+}++Status TableBuilder::status() const {+ return rep_->status;+}++Status TableBuilder::Finish() {+ Rep* r = rep_;+ Flush();+ assert(!r->closed);+ r->closed = true;+ BlockHandle metaindex_block_handle;+ BlockHandle index_block_handle;+ if (ok()) {+ BlockBuilder meta_index_block(&r->options);+ // TODO(postrelease): Add stats and other meta blocks+ WriteBlock(&meta_index_block, &metaindex_block_handle);+ }+ if (ok()) {+ if (r->pending_index_entry) {+ r->options.comparator->FindShortSuccessor(&r->last_key);+ std::string handle_encoding;+ r->pending_handle.EncodeTo(&handle_encoding);+ r->index_block.Add(r->last_key, Slice(handle_encoding));+ r->pending_index_entry = false;+ }+ WriteBlock(&r->index_block, &index_block_handle);+ }+ if (ok()) {+ Footer footer;+ footer.set_metaindex_handle(metaindex_block_handle);+ footer.set_index_handle(index_block_handle);+ std::string footer_encoding;+ footer.EncodeTo(&footer_encoding);+ r->status = r->file->Append(footer_encoding);+ if (r->status.ok()) {+ r->offset += footer_encoding.size();+ }+ }+ return r->status;+}++void TableBuilder::Abandon() {+ Rep* r = rep_;+ assert(!r->closed);+ r->closed = true;+}++uint64_t TableBuilder::NumEntries() const {+ return rep_->num_entries;+}++uint64_t TableBuilder::FileSize() const {+ return rep_->offset;+}++} // namespace leveldb
+ cbits/leveldb/table/table_test.cc view
@@ -0,0 +1,854 @@+// 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/table.h"++#include <map>+#include <string>+#include "db/dbformat.h"+#include "db/memtable.h"+#include "db/write_batch_internal.h"+#include "leveldb/db.h"+#include "leveldb/env.h"+#include "leveldb/iterator.h"+#include "leveldb/table_builder.h"+#include "table/block.h"+#include "table/block_builder.h"+#include "table/format.h"+#include "util/random.h"+#include "util/testharness.h"+#include "util/testutil.h"++namespace leveldb {++// Return reverse of "key".+// Used to test non-lexicographic comparators.+static std::string Reverse(const Slice& key) {+ std::string str(key.ToString());+ std::string rev("");+ for (std::string::reverse_iterator rit = str.rbegin();+ rit != str.rend(); ++rit) {+ rev.push_back(*rit);+ }+ return rev;+}++namespace {+class ReverseKeyComparator : public Comparator {+ public:+ virtual const char* Name() const {+ return "leveldb.ReverseBytewiseComparator";+ }++ virtual int Compare(const Slice& a, const Slice& b) const {+ return BytewiseComparator()->Compare(Reverse(a), Reverse(b));+ }++ virtual void FindShortestSeparator(+ std::string* start,+ const Slice& limit) const {+ std::string s = Reverse(*start);+ std::string l = Reverse(limit);+ BytewiseComparator()->FindShortestSeparator(&s, l);+ *start = Reverse(s);+ }++ virtual void FindShortSuccessor(std::string* key) const {+ std::string s = Reverse(*key);+ BytewiseComparator()->FindShortSuccessor(&s);+ *key = Reverse(s);+ }+};+} // namespace+static ReverseKeyComparator reverse_key_comparator;++static void Increment(const Comparator* cmp, std::string* key) {+ if (cmp == BytewiseComparator()) {+ key->push_back('\0');+ } else {+ assert(cmp == &reverse_key_comparator);+ std::string rev = Reverse(*key);+ rev.push_back('\0');+ *key = Reverse(rev);+ }+}++// An STL comparator that uses a Comparator+namespace {+struct STLLessThan {+ const Comparator* cmp;++ STLLessThan() : cmp(BytewiseComparator()) { }+ STLLessThan(const Comparator* c) : cmp(c) { }+ bool operator()(const std::string& a, const std::string& b) const {+ return cmp->Compare(Slice(a), Slice(b)) < 0;+ }+};+} // namespace++class StringSink: public WritableFile {+ public:+ ~StringSink() { }++ const std::string& contents() const { return contents_; }++ virtual Status Close() { return Status::OK(); }+ virtual Status Flush() { return Status::OK(); }+ virtual Status Sync() { return Status::OK(); }++ virtual Status Append(const Slice& data) {+ contents_.append(data.data(), data.size());+ return Status::OK();+ }++ private:+ std::string contents_;+};+++class StringSource: public RandomAccessFile {+ public:+ StringSource(const Slice& contents)+ : contents_(contents.data(), contents.size()) {+ }++ virtual ~StringSource() { }++ uint64_t Size() const { return contents_.size(); }++ virtual Status Read(uint64_t offset, size_t n, Slice* result,+ char* scratch) const {+ if (offset > contents_.size()) {+ return Status::InvalidArgument("invalid Read offset");+ }+ if (offset + n > contents_.size()) {+ n = contents_.size() - offset;+ }+ memcpy(scratch, &contents_[offset], n);+ *result = Slice(scratch, n);+ return Status::OK();+ }++ private:+ std::string contents_;+};++typedef std::map<std::string, std::string, STLLessThan> KVMap;++// Helper class for tests to unify the interface between+// BlockBuilder/TableBuilder and Block/Table.+class Constructor {+ public:+ explicit Constructor(const Comparator* cmp) : data_(STLLessThan(cmp)) { }+ virtual ~Constructor() { }++ void Add(const std::string& key, const Slice& value) {+ data_[key] = value.ToString();+ }++ // Finish constructing the data structure with all the keys that have+ // been added so far. Returns the keys in sorted order in "*keys"+ // and stores the key/value pairs in "*kvmap"+ void Finish(const Options& options,+ std::vector<std::string>* keys,+ KVMap* kvmap) {+ *kvmap = data_;+ keys->clear();+ for (KVMap::const_iterator it = data_.begin();+ it != data_.end();+ ++it) {+ keys->push_back(it->first);+ }+ data_.clear();+ Status s = FinishImpl(options, *kvmap);+ ASSERT_TRUE(s.ok()) << s.ToString();+ }++ // Construct the data structure from the data in "data"+ virtual Status FinishImpl(const Options& options, const KVMap& data) = 0;++ virtual size_t NumBytes() const = 0;++ virtual Iterator* NewIterator() const = 0;++ virtual const KVMap& data() { return data_; }++ virtual DB* db() const { return NULL; } // Overridden in DBConstructor++ private:+ KVMap data_;+};++class BlockConstructor: public Constructor {+ public:+ explicit BlockConstructor(const Comparator* cmp)+ : Constructor(cmp),+ comparator_(cmp),+ block_size_(-1),+ block_(NULL) { }+ ~BlockConstructor() {+ delete block_;+ }+ virtual Status FinishImpl(const Options& options, const KVMap& data) {+ delete block_;+ block_ = NULL;+ BlockBuilder builder(&options);++ for (KVMap::const_iterator it = data.begin();+ it != data.end();+ ++it) {+ builder.Add(it->first, it->second);+ }+ // Open the block+ Slice block_data = builder.Finish();+ block_size_ = block_data.size();+ char* block_data_copy = new char[block_size_];+ memcpy(block_data_copy, block_data.data(), block_size_);+ block_ = new Block(block_data_copy, block_size_);+ return Status::OK();+ }+ virtual size_t NumBytes() const { return block_size_; }++ virtual Iterator* NewIterator() const {+ return block_->NewIterator(comparator_);+ }++ private:+ const Comparator* comparator_;+ int block_size_;+ Block* block_;++ BlockConstructor();+};++class TableConstructor: public Constructor {+ public:+ TableConstructor(const Comparator* cmp)+ : Constructor(cmp),+ source_(NULL), table_(NULL) {+ }+ ~TableConstructor() {+ Reset();+ }+ virtual Status FinishImpl(const Options& options, const KVMap& data) {+ Reset();+ StringSink sink;+ TableBuilder builder(options, &sink);++ for (KVMap::const_iterator it = data.begin();+ it != data.end();+ ++it) {+ builder.Add(it->first, it->second);+ ASSERT_TRUE(builder.status().ok());+ }+ Status s = builder.Finish();+ ASSERT_TRUE(s.ok()) << s.ToString();++ ASSERT_EQ(sink.contents().size(), builder.FileSize());++ // Open the table+ source_ = new StringSource(sink.contents());+ Options table_options;+ table_options.comparator = options.comparator;+ return Table::Open(table_options, source_, sink.contents().size(), &table_);+ }+ virtual size_t NumBytes() const { return source_->Size(); }++ virtual Iterator* NewIterator() const {+ return table_->NewIterator(ReadOptions());+ }++ uint64_t ApproximateOffsetOf(const Slice& key) const {+ return table_->ApproximateOffsetOf(key);+ }++ private:+ void Reset() {+ delete table_;+ delete source_;+ table_ = NULL;+ source_ = NULL;+ }++ StringSource* source_;+ Table* table_;++ TableConstructor();+};++// A helper class that converts internal format keys into user keys+class KeyConvertingIterator: public Iterator {+ public:+ explicit KeyConvertingIterator(Iterator* iter) : iter_(iter) { }+ virtual ~KeyConvertingIterator() { delete iter_; }+ virtual bool Valid() const { return iter_->Valid(); }+ virtual void Seek(const Slice& target) {+ ParsedInternalKey ikey(target, kMaxSequenceNumber, kTypeValue);+ std::string encoded;+ AppendInternalKey(&encoded, ikey);+ iter_->Seek(encoded);+ }+ virtual void SeekToFirst() { iter_->SeekToFirst(); }+ virtual void SeekToLast() { iter_->SeekToLast(); }+ virtual void Next() { iter_->Next(); }+ virtual void Prev() { iter_->Prev(); }++ virtual Slice key() const {+ assert(Valid());+ ParsedInternalKey key;+ if (!ParseInternalKey(iter_->key(), &key)) {+ status_ = Status::Corruption("malformed internal key");+ return Slice("corrupted key");+ }+ return key.user_key;+ }++ virtual Slice value() const { return iter_->value(); }+ virtual Status status() const {+ return status_.ok() ? iter_->status() : status_;+ }++ private:+ mutable Status status_;+ Iterator* iter_;++ // No copying allowed+ KeyConvertingIterator(const KeyConvertingIterator&);+ void operator=(const KeyConvertingIterator&);+};++class MemTableConstructor: public Constructor {+ public:+ explicit MemTableConstructor(const Comparator* cmp)+ : Constructor(cmp),+ internal_comparator_(cmp) {+ memtable_ = new MemTable(internal_comparator_);+ memtable_->Ref();+ }+ ~MemTableConstructor() {+ memtable_->Unref();+ }+ virtual Status FinishImpl(const Options& options, const KVMap& data) {+ memtable_->Unref();+ memtable_ = new MemTable(internal_comparator_);+ memtable_->Ref();+ int seq = 1;+ for (KVMap::const_iterator it = data.begin();+ it != data.end();+ ++it) {+ memtable_->Add(seq, kTypeValue, it->first, it->second);+ seq++;+ }+ return Status::OK();+ }+ virtual size_t NumBytes() const {+ return memtable_->ApproximateMemoryUsage();+ }++ virtual Iterator* NewIterator() const {+ return new KeyConvertingIterator(memtable_->NewIterator());+ }++ private:+ InternalKeyComparator internal_comparator_;+ MemTable* memtable_;+};++class DBConstructor: public Constructor {+ public:+ explicit DBConstructor(const Comparator* cmp)+ : Constructor(cmp),+ comparator_(cmp) {+ db_ = NULL;+ NewDB();+ }+ ~DBConstructor() {+ delete db_;+ }+ virtual Status FinishImpl(const Options& options, const KVMap& data) {+ delete db_;+ db_ = NULL;+ NewDB();+ for (KVMap::const_iterator it = data.begin();+ it != data.end();+ ++it) {+ WriteBatch batch;+ batch.Put(it->first, it->second);+ ASSERT_TRUE(db_->Write(WriteOptions(), &batch).ok());+ }+ return Status::OK();+ }+ virtual size_t NumBytes() const {+ Range r("", "\xff\xff");+ uint64_t size;+ db_->GetApproximateSizes(&r, 1, &size);+ return size;+ }++ virtual Iterator* NewIterator() const {+ return db_->NewIterator(ReadOptions());+ }++ virtual DB* db() const { return db_; }++ private:+ void NewDB() {+ std::string name = test::TmpDir() + "/table_testdb";++ Options options;+ options.comparator = comparator_;+ Status status = DestroyDB(name, options);+ ASSERT_TRUE(status.ok()) << status.ToString();++ options.create_if_missing = true;+ options.error_if_exists = true;+ options.write_buffer_size = 10000; // Something small to force merging+ status = DB::Open(options, name, &db_);+ ASSERT_TRUE(status.ok()) << status.ToString();+ }++ const Comparator* comparator_;+ DB* db_;+};++enum TestType {+ TABLE_TEST,+ BLOCK_TEST,+ MEMTABLE_TEST,+ DB_TEST+};++struct TestArgs {+ TestType type;+ bool reverse_compare;+ int restart_interval;+};++static const TestArgs kTestArgList[] = {+ { TABLE_TEST, false, 16 },+ { TABLE_TEST, false, 1 },+ { TABLE_TEST, false, 1024 },+ { TABLE_TEST, true, 16 },+ { TABLE_TEST, true, 1 },+ { TABLE_TEST, true, 1024 },++ { BLOCK_TEST, false, 16 },+ { BLOCK_TEST, false, 1 },+ { BLOCK_TEST, false, 1024 },+ { BLOCK_TEST, true, 16 },+ { BLOCK_TEST, true, 1 },+ { BLOCK_TEST, true, 1024 },++ // Restart interval does not matter for memtables+ { MEMTABLE_TEST, false, 16 },+ { MEMTABLE_TEST, true, 16 },++ // Do not bother with restart interval variations for DB+ { DB_TEST, false, 16 },+ { DB_TEST, true, 16 },+};+static const int kNumTestArgs = sizeof(kTestArgList) / sizeof(kTestArgList[0]);++class Harness {+ public:+ Harness() : constructor_(NULL) { }++ void Init(const TestArgs& args) {+ delete constructor_;+ constructor_ = NULL;+ options_ = Options();++ options_.block_restart_interval = args.restart_interval;+ // Use shorter block size for tests to exercise block boundary+ // conditions more.+ options_.block_size = 256;+ if (args.reverse_compare) {+ options_.comparator = &reverse_key_comparator;+ }+ switch (args.type) {+ case TABLE_TEST:+ constructor_ = new TableConstructor(options_.comparator);+ break;+ case BLOCK_TEST:+ constructor_ = new BlockConstructor(options_.comparator);+ break;+ case MEMTABLE_TEST:+ constructor_ = new MemTableConstructor(options_.comparator);+ break;+ case DB_TEST:+ constructor_ = new DBConstructor(options_.comparator);+ break;+ }+ }++ ~Harness() {+ delete constructor_;+ }++ void Add(const std::string& key, const std::string& value) {+ constructor_->Add(key, value);+ }++ void Test(Random* rnd) {+ std::vector<std::string> keys;+ KVMap data;+ constructor_->Finish(options_, &keys, &data);++ TestForwardScan(keys, data);+ TestBackwardScan(keys, data);+ TestRandomAccess(rnd, keys, data);+ }++ void TestForwardScan(const std::vector<std::string>& keys,+ const KVMap& data) {+ Iterator* iter = constructor_->NewIterator();+ ASSERT_TRUE(!iter->Valid());+ iter->SeekToFirst();+ for (KVMap::const_iterator model_iter = data.begin();+ model_iter != data.end();+ ++model_iter) {+ ASSERT_EQ(ToString(data, model_iter), ToString(iter));+ iter->Next();+ }+ ASSERT_TRUE(!iter->Valid());+ delete iter;+ }++ void TestBackwardScan(const std::vector<std::string>& keys,+ const KVMap& data) {+ Iterator* iter = constructor_->NewIterator();+ ASSERT_TRUE(!iter->Valid());+ iter->SeekToLast();+ for (KVMap::const_reverse_iterator model_iter = data.rbegin();+ model_iter != data.rend();+ ++model_iter) {+ ASSERT_EQ(ToString(data, model_iter), ToString(iter));+ iter->Prev();+ }+ ASSERT_TRUE(!iter->Valid());+ delete iter;+ }++ void TestRandomAccess(Random* rnd,+ const std::vector<std::string>& keys,+ const KVMap& data) {+ static const bool kVerbose = false;+ Iterator* iter = constructor_->NewIterator();+ ASSERT_TRUE(!iter->Valid());+ KVMap::const_iterator model_iter = data.begin();+ if (kVerbose) fprintf(stderr, "---\n");+ for (int i = 0; i < 200; i++) {+ const int toss = rnd->Uniform(5);+ switch (toss) {+ case 0: {+ if (iter->Valid()) {+ if (kVerbose) fprintf(stderr, "Next\n");+ iter->Next();+ ++model_iter;+ ASSERT_EQ(ToString(data, model_iter), ToString(iter));+ }+ break;+ }++ case 1: {+ if (kVerbose) fprintf(stderr, "SeekToFirst\n");+ iter->SeekToFirst();+ model_iter = data.begin();+ ASSERT_EQ(ToString(data, model_iter), ToString(iter));+ break;+ }++ case 2: {+ std::string key = PickRandomKey(rnd, keys);+ model_iter = data.lower_bound(key);+ if (kVerbose) fprintf(stderr, "Seek '%s'\n",+ EscapeString(key).c_str());+ iter->Seek(Slice(key));+ ASSERT_EQ(ToString(data, model_iter), ToString(iter));+ break;+ }++ case 3: {+ if (iter->Valid()) {+ if (kVerbose) fprintf(stderr, "Prev\n");+ iter->Prev();+ if (model_iter == data.begin()) {+ model_iter = data.end(); // Wrap around to invalid value+ } else {+ --model_iter;+ }+ ASSERT_EQ(ToString(data, model_iter), ToString(iter));+ }+ break;+ }++ case 4: {+ if (kVerbose) fprintf(stderr, "SeekToLast\n");+ iter->SeekToLast();+ if (keys.empty()) {+ model_iter = data.end();+ } else {+ std::string last = data.rbegin()->first;+ model_iter = data.lower_bound(last);+ }+ ASSERT_EQ(ToString(data, model_iter), ToString(iter));+ break;+ }+ }+ }+ delete iter;+ }++ std::string ToString(const KVMap& data, const KVMap::const_iterator& it) {+ if (it == data.end()) {+ return "END";+ } else {+ return "'" + it->first + "->" + it->second + "'";+ }+ }++ std::string ToString(const KVMap& data,+ const KVMap::const_reverse_iterator& it) {+ if (it == data.rend()) {+ return "END";+ } else {+ return "'" + it->first + "->" + it->second + "'";+ }+ }++ std::string ToString(const Iterator* it) {+ if (!it->Valid()) {+ return "END";+ } else {+ return "'" + it->key().ToString() + "->" + it->value().ToString() + "'";+ }+ }++ std::string PickRandomKey(Random* rnd, const std::vector<std::string>& keys) {+ if (keys.empty()) {+ return "foo";+ } else {+ const int index = rnd->Uniform(keys.size());+ std::string result = keys[index];+ switch (rnd->Uniform(3)) {+ case 0:+ // Return an existing key+ break;+ case 1: {+ // Attempt to return something smaller than an existing key+ if (result.size() > 0 && result[result.size()-1] > '\0') {+ result[result.size()-1]--;+ }+ break;+ }+ case 2: {+ // Return something larger than an existing key+ Increment(options_.comparator, &result);+ break;+ }+ }+ return result;+ }+ }++ // Returns NULL if not running against a DB+ DB* db() const { return constructor_->db(); }++ private:+ Options options_;+ Constructor* constructor_;+};++// Test the empty key+TEST(Harness, SimpleEmptyKey) {+ for (int i = 0; i < kNumTestArgs; i++) {+ Init(kTestArgList[i]);+ Random rnd(test::RandomSeed() + 1);+ Add("", "v");+ Test(&rnd);+ }+}++TEST(Harness, SimpleSingle) {+ for (int i = 0; i < kNumTestArgs; i++) {+ Init(kTestArgList[i]);+ Random rnd(test::RandomSeed() + 2);+ Add("abc", "v");+ Test(&rnd);+ }+}++TEST(Harness, SimpleMulti) {+ for (int i = 0; i < kNumTestArgs; i++) {+ Init(kTestArgList[i]);+ Random rnd(test::RandomSeed() + 3);+ Add("abc", "v");+ Add("abcd", "v");+ Add("ac", "v2");+ Test(&rnd);+ }+}++TEST(Harness, SimpleSpecialKey) {+ for (int i = 0; i < kNumTestArgs; i++) {+ Init(kTestArgList[i]);+ Random rnd(test::RandomSeed() + 4);+ Add("\xff\xff", "v3");+ Test(&rnd);+ }+}++TEST(Harness, Randomized) {+ for (int i = 0; i < kNumTestArgs; i++) {+ Init(kTestArgList[i]);+ Random rnd(test::RandomSeed() + 5);+ for (int num_entries = 0; num_entries < 2000;+ num_entries += (num_entries < 50 ? 1 : 200)) {+ if ((num_entries % 10) == 0) {+ fprintf(stderr, "case %d of %d: num_entries = %d\n",+ (i + 1), int(kNumTestArgs), num_entries);+ }+ for (int e = 0; e < num_entries; e++) {+ std::string v;+ Add(test::RandomKey(&rnd, rnd.Skewed(4)),+ test::RandomString(&rnd, rnd.Skewed(5), &v).ToString());+ }+ Test(&rnd);+ }+ }+}++TEST(Harness, RandomizedLongDB) {+ Random rnd(test::RandomSeed());+ TestArgs args = { DB_TEST, false, 16 };+ Init(args);+ int num_entries = 100000;+ for (int e = 0; e < num_entries; e++) {+ std::string v;+ Add(test::RandomKey(&rnd, rnd.Skewed(4)),+ test::RandomString(&rnd, rnd.Skewed(5), &v).ToString());+ }+ Test(&rnd);++ // We must have created enough data to force merging+ int files = 0;+ for (int level = 0; level < config::kNumLevels; level++) {+ std::string value;+ char name[100];+ snprintf(name, sizeof(name), "leveldb.num-files-at-level%d", level);+ ASSERT_TRUE(db()->GetProperty(name, &value));+ files += atoi(value.c_str());+ }+ ASSERT_GT(files, 0);+}++class MemTableTest { };++TEST(MemTableTest, Simple) {+ InternalKeyComparator cmp(BytewiseComparator());+ MemTable* memtable = new MemTable(cmp);+ memtable->Ref();+ WriteBatch batch;+ WriteBatchInternal::SetSequence(&batch, 100);+ batch.Put(std::string("k1"), std::string("v1"));+ batch.Put(std::string("k2"), std::string("v2"));+ batch.Put(std::string("k3"), std::string("v3"));+ batch.Put(std::string("largekey"), std::string("vlarge"));+ ASSERT_TRUE(WriteBatchInternal::InsertInto(&batch, memtable).ok());++ Iterator* iter = memtable->NewIterator();+ iter->SeekToFirst();+ while (iter->Valid()) {+ fprintf(stderr, "key: '%s' -> '%s'\n",+ iter->key().ToString().c_str(),+ iter->value().ToString().c_str());+ iter->Next();+ }++ delete iter;+ memtable->Unref();+}++static bool Between(uint64_t val, uint64_t low, uint64_t high) {+ bool result = (val >= low) && (val <= high);+ if (!result) {+ fprintf(stderr, "Value %llu is not in range [%llu, %llu]\n",+ (unsigned long long)(val),+ (unsigned long long)(low),+ (unsigned long long)(high));+ }+ return result;+}++class TableTest { };++TEST(TableTest, ApproximateOffsetOfPlain) {+ TableConstructor c(BytewiseComparator());+ c.Add("k01", "hello");+ c.Add("k02", "hello2");+ c.Add("k03", std::string(10000, 'x'));+ c.Add("k04", std::string(200000, 'x'));+ c.Add("k05", std::string(300000, 'x'));+ c.Add("k06", "hello3");+ c.Add("k07", std::string(100000, 'x'));+ std::vector<std::string> keys;+ KVMap kvmap;+ Options options;+ options.block_size = 1024;+ options.compression = kNoCompression;+ c.Finish(options, &keys, &kvmap);++ ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01a"), 0, 0));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 0, 0));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 10000, 11000));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04a"), 210000, 211000));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k05"), 210000, 211000));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k06"), 510000, 511000));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k07"), 510000, 511000));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 610000, 611000));++}++static bool SnappyCompressionSupported() {+ std::string out;+ Slice in = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa";+ return port::Snappy_Compress(in.data(), in.size(), &out);+}++TEST(TableTest, ApproximateOffsetOfCompressed) {+ if (!SnappyCompressionSupported()) {+ fprintf(stderr, "skipping compression tests\n");+ return;+ }++ Random rnd(301);+ TableConstructor c(BytewiseComparator());+ std::string tmp;+ c.Add("k01", "hello");+ c.Add("k02", test::CompressibleString(&rnd, 0.25, 10000, &tmp));+ c.Add("k03", "hello3");+ c.Add("k04", test::CompressibleString(&rnd, 0.25, 10000, &tmp));+ std::vector<std::string> keys;+ KVMap kvmap;+ Options options;+ options.block_size = 1024;+ options.compression = kSnappyCompression;+ c.Finish(options, &keys, &kvmap);++ ASSERT_TRUE(Between(c.ApproximateOffsetOf("abc"), 0, 0));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k01"), 0, 0));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k02"), 0, 0));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k03"), 2000, 3000));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("k04"), 2000, 3000));+ ASSERT_TRUE(Between(c.ApproximateOffsetOf("xyz"), 4000, 6000));+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/table/two_level_iterator.cc view
@@ -0,0 +1,182 @@+// 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 "table/two_level_iterator.h"++#include "leveldb/table.h"+#include "table/block.h"+#include "table/format.h"+#include "table/iterator_wrapper.h"++namespace leveldb {++namespace {++typedef Iterator* (*BlockFunction)(void*, const ReadOptions&, const Slice&);++class TwoLevelIterator: public Iterator {+ public:+ TwoLevelIterator(+ Iterator* index_iter,+ BlockFunction block_function,+ void* arg,+ const ReadOptions& options);++ virtual ~TwoLevelIterator();++ virtual void Seek(const Slice& target);+ virtual void SeekToFirst();+ virtual void SeekToLast();+ virtual void Next();+ virtual void Prev();++ virtual bool Valid() const {+ return data_iter_.Valid();+ }+ virtual Slice key() const {+ assert(Valid());+ return data_iter_.key();+ }+ virtual Slice value() const {+ assert(Valid());+ return data_iter_.value();+ }+ virtual Status status() const {+ // It'd be nice if status() returned a const Status& instead of a Status+ if (!index_iter_.status().ok()) {+ return index_iter_.status();+ } else if (data_iter_.iter() != NULL && !data_iter_.status().ok()) {+ return data_iter_.status();+ } else {+ return status_;+ }+ }++ private:+ void SaveError(const Status& s) {+ if (status_.ok() && !s.ok()) status_ = s;+ }+ void SkipEmptyDataBlocksForward();+ void SkipEmptyDataBlocksBackward();+ void SetDataIterator(Iterator* data_iter);+ void InitDataBlock();++ BlockFunction block_function_;+ void* arg_;+ const ReadOptions options_;+ Status status_;+ IteratorWrapper index_iter_;+ IteratorWrapper data_iter_; // May be NULL+ // If data_iter_ is non-NULL, then "data_block_handle_" holds the+ // "index_value" passed to block_function_ to create the data_iter_.+ std::string data_block_handle_;+};++TwoLevelIterator::TwoLevelIterator(+ Iterator* index_iter,+ BlockFunction block_function,+ void* arg,+ const ReadOptions& options)+ : block_function_(block_function),+ arg_(arg),+ options_(options),+ index_iter_(index_iter),+ data_iter_(NULL) {+}++TwoLevelIterator::~TwoLevelIterator() {+}++void TwoLevelIterator::Seek(const Slice& target) {+ index_iter_.Seek(target);+ InitDataBlock();+ if (data_iter_.iter() != NULL) data_iter_.Seek(target);+ SkipEmptyDataBlocksForward();+}++void TwoLevelIterator::SeekToFirst() {+ index_iter_.SeekToFirst();+ InitDataBlock();+ if (data_iter_.iter() != NULL) data_iter_.SeekToFirst();+ SkipEmptyDataBlocksForward();+}++void TwoLevelIterator::SeekToLast() {+ index_iter_.SeekToLast();+ InitDataBlock();+ if (data_iter_.iter() != NULL) data_iter_.SeekToLast();+ SkipEmptyDataBlocksBackward();+}++void TwoLevelIterator::Next() {+ assert(Valid());+ data_iter_.Next();+ SkipEmptyDataBlocksForward();+}++void TwoLevelIterator::Prev() {+ assert(Valid());+ data_iter_.Prev();+ SkipEmptyDataBlocksBackward();+}+++void TwoLevelIterator::SkipEmptyDataBlocksForward() {+ while (data_iter_.iter() == NULL || !data_iter_.Valid()) {+ // Move to next block+ if (!index_iter_.Valid()) {+ SetDataIterator(NULL);+ return;+ }+ index_iter_.Next();+ InitDataBlock();+ if (data_iter_.iter() != NULL) data_iter_.SeekToFirst();+ }+}++void TwoLevelIterator::SkipEmptyDataBlocksBackward() {+ while (data_iter_.iter() == NULL || !data_iter_.Valid()) {+ // Move to next block+ if (!index_iter_.Valid()) {+ SetDataIterator(NULL);+ return;+ }+ index_iter_.Prev();+ InitDataBlock();+ if (data_iter_.iter() != NULL) data_iter_.SeekToLast();+ }+}++void TwoLevelIterator::SetDataIterator(Iterator* data_iter) {+ if (data_iter_.iter() != NULL) SaveError(data_iter_.status());+ data_iter_.Set(data_iter);+}++void TwoLevelIterator::InitDataBlock() {+ if (!index_iter_.Valid()) {+ SetDataIterator(NULL);+ } else {+ Slice handle = index_iter_.value();+ if (data_iter_.iter() != NULL && handle.compare(data_block_handle_) == 0) {+ // data_iter_ is already constructed with this iterator, so+ // no need to change anything+ } else {+ Iterator* iter = (*block_function_)(arg_, options_, handle);+ data_block_handle_.assign(handle.data(), handle.size());+ SetDataIterator(iter);+ }+ }+}++} // namespace++Iterator* NewTwoLevelIterator(+ Iterator* index_iter,+ BlockFunction block_function,+ void* arg,+ const ReadOptions& options) {+ return new TwoLevelIterator(index_iter, block_function, arg, options);+}++} // namespace leveldb
+ cbits/leveldb/table/two_level_iterator.h view
@@ -0,0 +1,34 @@+// 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.++#ifndef STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_+#define STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_++#include "leveldb/iterator.h"++namespace leveldb {++struct ReadOptions;++// Return a new two level iterator. A two-level iterator contains an+// index iterator whose values point to a sequence of blocks where+// each block is itself a sequence of key,value pairs. The returned+// two-level iterator yields the concatenation of all key/value pairs+// in the sequence of blocks. Takes ownership of "index_iter" and+// will delete it when no longer needed.+//+// Uses a supplied function to convert an index_iter value into+// an iterator over the contents of the corresponding block.+extern Iterator* NewTwoLevelIterator(+ Iterator* index_iter,+ Iterator* (*block_function)(+ void* arg,+ const ReadOptions& options,+ const Slice& index_value),+ void* arg,+ const ReadOptions& options);++} // namespace leveldb++#endif // STORAGE_LEVELDB_TABLE_TWO_LEVEL_ITERATOR_H_
+ cbits/leveldb/util/arena.cc view
@@ -0,0 +1,68 @@+// 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 "util/arena.h"+#include <assert.h>++namespace leveldb {++static const int kBlockSize = 4096;++Arena::Arena() {+ blocks_memory_ = 0;+ alloc_ptr_ = NULL; // First allocation will allocate a block+ alloc_bytes_remaining_ = 0;+}++Arena::~Arena() {+ for (size_t i = 0; i < blocks_.size(); i++) {+ delete[] blocks_[i];+ }+}++char* Arena::AllocateFallback(size_t bytes) {+ if (bytes > kBlockSize / 4) {+ // Object is more than a quarter of our block size. Allocate it separately+ // to avoid wasting too much space in leftover bytes.+ char* result = AllocateNewBlock(bytes);+ return result;+ }++ // We waste the remaining space in the current block.+ alloc_ptr_ = AllocateNewBlock(kBlockSize);+ alloc_bytes_remaining_ = kBlockSize;++ char* result = alloc_ptr_;+ alloc_ptr_ += bytes;+ alloc_bytes_remaining_ -= bytes;+ return result;+}++char* Arena::AllocateAligned(size_t bytes) {+ const int align = sizeof(void*); // We'll align to pointer size+ assert((align & (align-1)) == 0); // Pointer size should be a power of 2+ size_t current_mod = reinterpret_cast<uintptr_t>(alloc_ptr_) & (align-1);+ size_t slop = (current_mod == 0 ? 0 : align - current_mod);+ size_t needed = bytes + slop;+ char* result;+ if (needed <= alloc_bytes_remaining_) {+ result = alloc_ptr_ + slop;+ alloc_ptr_ += needed;+ alloc_bytes_remaining_ -= needed;+ } else {+ // AllocateFallback always returned aligned memory+ result = AllocateFallback(bytes);+ }+ assert((reinterpret_cast<uintptr_t>(result) & (align-1)) == 0);+ return result;+}++char* Arena::AllocateNewBlock(size_t block_bytes) {+ char* result = new char[block_bytes];+ blocks_memory_ += block_bytes;+ blocks_.push_back(result);+ return result;+}++} // namespace leveldb
+ cbits/leveldb/util/arena.h view
@@ -0,0 +1,68 @@+// 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.++#ifndef STORAGE_LEVELDB_UTIL_ARENA_H_+#define STORAGE_LEVELDB_UTIL_ARENA_H_++#include <cstddef>+#include <vector>+#include <assert.h>+#include <stdint.h>++namespace leveldb {++class Arena {+ public:+ Arena();+ ~Arena();++ // Return a pointer to a newly allocated memory block of "bytes" bytes.+ char* Allocate(size_t bytes);++ // Allocate memory with the normal alignment guarantees provided by malloc+ char* AllocateAligned(size_t bytes);++ // Returns an estimate of the total memory usage of data allocated+ // by the arena (including space allocated but not yet used for user+ // allocations).+ size_t MemoryUsage() const {+ return blocks_memory_ + blocks_.capacity() * sizeof(char*);+ }++ private:+ char* AllocateFallback(size_t bytes);+ char* AllocateNewBlock(size_t block_bytes);++ // Allocation state+ char* alloc_ptr_;+ size_t alloc_bytes_remaining_;++ // Array of new[] allocated memory blocks+ std::vector<char*> blocks_;++ // Bytes of memory in blocks allocated so far+ size_t blocks_memory_;++ // No copying allowed+ Arena(const Arena&);+ void operator=(const Arena&);+};++inline char* Arena::Allocate(size_t bytes) {+ // The semantics of what to return are a bit messy if we allow+ // 0-byte allocations, so we disallow them here (we don't need+ // them for our internal use).+ assert(bytes > 0);+ if (bytes <= alloc_bytes_remaining_) {+ char* result = alloc_ptr_;+ alloc_ptr_ += bytes;+ alloc_bytes_remaining_ -= bytes;+ return result;+ }+ return AllocateFallback(bytes);+}++} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_ARENA_H_
+ cbits/leveldb/util/arena_test.cc view
@@ -0,0 +1,68 @@+// 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 "util/arena.h"++#include "util/random.h"+#include "util/testharness.h"++namespace leveldb {++class ArenaTest { };++TEST(ArenaTest, Empty) {+ Arena arena;+}++TEST(ArenaTest, Simple) {+ std::vector<std::pair<size_t, char*> > allocated;+ Arena arena;+ const int N = 100000;+ size_t bytes = 0;+ Random rnd(301);+ for (int i = 0; i < N; i++) {+ size_t s;+ if (i % (N / 10) == 0) {+ s = i;+ } else {+ s = rnd.OneIn(4000) ? rnd.Uniform(6000) :+ (rnd.OneIn(10) ? rnd.Uniform(100) : rnd.Uniform(20));+ }+ if (s == 0) {+ // Our arena disallows size 0 allocations.+ s = 1;+ }+ char* r;+ if (rnd.OneIn(10)) {+ r = arena.AllocateAligned(s);+ } else {+ r = arena.Allocate(s);+ }++ for (int b = 0; b < s; b++) {+ // Fill the "i"th allocation with a known bit pattern+ r[b] = i % 256;+ }+ bytes += s;+ allocated.push_back(std::make_pair(s, r));+ ASSERT_GE(arena.MemoryUsage(), bytes);+ if (i > N/10) {+ ASSERT_LE(arena.MemoryUsage(), bytes * 1.10);+ }+ }+ for (int i = 0; i < allocated.size(); i++) {+ size_t num_bytes = allocated[i].first;+ const char* p = allocated[i].second;+ for (int b = 0; b < num_bytes; b++) {+ // Check the "i"th allocation for the known bit pattern+ ASSERT_EQ(int(p[b]) & 0xff, i % 256);+ }+ }+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/util/cache.cc view
@@ -0,0 +1,328 @@+// 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 <assert.h>+#include <stdio.h>+#include <stdlib.h>++#include "leveldb/cache.h"+#include "port/port.h"+#include "util/hash.h"+#include "util/mutexlock.h"++namespace leveldb {++Cache::~Cache() {+}++namespace {++// LRU cache implementation++// An entry is a variable length heap-allocated structure. Entries+// are kept in a circular doubly linked list ordered by access time.+struct LRUHandle {+ void* value;+ void (*deleter)(const Slice&, void* value);+ LRUHandle* next_hash;+ LRUHandle* next;+ LRUHandle* prev;+ size_t charge; // TODO(opt): Only allow uint32_t?+ size_t key_length;+ uint32_t refs;+ uint32_t hash; // Hash of key(); used for fast sharding and comparisons+ char key_data[1]; // Beginning of key++ Slice key() const {+ // For cheaper lookups, we allow a temporary Handle object+ // to store a pointer to a key in "value".+ if (next == this) {+ return *(reinterpret_cast<Slice*>(value));+ } else {+ return Slice(key_data, key_length);+ }+ }+};++// We provide our own simple hash table since it removes a whole bunch+// of porting hacks and is also faster than some of the built-in hash+// table implementations in some of the compiler/runtime combinations+// we have tested. E.g., readrandom speeds up by ~5% over the g+++// 4.4.3's builtin hashtable.+class HandleTable {+ public:+ HandleTable() : length_(0), elems_(0), list_(NULL) { Resize(); }+ ~HandleTable() { delete[] list_; }++ LRUHandle* Lookup(const Slice& key, uint32_t hash) {+ return *FindPointer(key, hash);+ }++ LRUHandle* Insert(LRUHandle* h) {+ LRUHandle** ptr = FindPointer(h->key(), h->hash);+ LRUHandle* old = *ptr;+ h->next_hash = (old == NULL ? NULL : old->next_hash);+ *ptr = h;+ if (old == NULL) {+ ++elems_;+ if (elems_ > length_) {+ // Since each cache entry is fairly large, we aim for a small+ // average linked list length (<= 1).+ Resize();+ }+ }+ return old;+ }++ LRUHandle* Remove(const Slice& key, uint32_t hash) {+ LRUHandle** ptr = FindPointer(key, hash);+ LRUHandle* result = *ptr;+ if (result != NULL) {+ *ptr = result->next_hash;+ --elems_;+ }+ return result;+ }++ private:+ // The table consists of an array of buckets where each bucket is+ // a linked list of cache entries that hash into the bucket.+ uint32_t length_;+ uint32_t elems_;+ LRUHandle** list_;++ // Return a pointer to slot that points to a cache entry that+ // matches key/hash. If there is no such cache entry, return a+ // pointer to the trailing slot in the corresponding linked list.+ LRUHandle** FindPointer(const Slice& key, uint32_t hash) {+ LRUHandle** ptr = &list_[hash & (length_ - 1)];+ while (*ptr != NULL &&+ ((*ptr)->hash != hash || key != (*ptr)->key())) {+ ptr = &(*ptr)->next_hash;+ }+ return ptr;+ }++ void Resize() {+ uint32_t new_length = 4;+ while (new_length < elems_) {+ new_length *= 2;+ }+ LRUHandle** new_list = new LRUHandle*[new_length];+ memset(new_list, 0, sizeof(new_list[0]) * new_length);+ uint32_t count = 0;+ for (uint32_t i = 0; i < length_; i++) {+ LRUHandle* h = list_[i];+ while (h != NULL) {+ LRUHandle* next = h->next_hash;+ Slice key = h->key();+ uint32_t hash = h->hash;+ LRUHandle** ptr = &new_list[hash & (new_length - 1)];+ h->next_hash = *ptr;+ *ptr = h;+ h = next;+ count++;+ }+ }+ assert(elems_ == count);+ delete[] list_;+ list_ = new_list;+ length_ = new_length;+ }+};++// A single shard of sharded cache.+class LRUCache {+ public:+ LRUCache();+ ~LRUCache();++ // Separate from constructor so caller can easily make an array of LRUCache+ void SetCapacity(size_t capacity) { capacity_ = capacity; }++ // Like Cache methods, but with an extra "hash" parameter.+ Cache::Handle* Insert(const Slice& key, uint32_t hash,+ void* value, size_t charge,+ void (*deleter)(const Slice& key, void* value));+ Cache::Handle* Lookup(const Slice& key, uint32_t hash);+ void Release(Cache::Handle* handle);+ void Erase(const Slice& key, uint32_t hash);++ private:+ void LRU_Remove(LRUHandle* e);+ void LRU_Append(LRUHandle* e);+ void Unref(LRUHandle* e);++ // Initialized before use.+ size_t capacity_;++ // mutex_ protects the following state.+ port::Mutex mutex_;+ size_t usage_;+ uint64_t last_id_;++ // Dummy head of LRU list.+ // lru.prev is newest entry, lru.next is oldest entry.+ LRUHandle lru_;++ HandleTable table_;+};++LRUCache::LRUCache()+ : usage_(0),+ last_id_(0) {+ // Make empty circular linked list+ lru_.next = &lru_;+ lru_.prev = &lru_;+}++LRUCache::~LRUCache() {+ for (LRUHandle* e = lru_.next; e != &lru_; ) {+ LRUHandle* next = e->next;+ assert(e->refs == 1); // Error if caller has an unreleased handle+ Unref(e);+ e = next;+ }+}++void LRUCache::Unref(LRUHandle* e) {+ assert(e->refs > 0);+ e->refs--;+ if (e->refs <= 0) {+ usage_ -= e->charge;+ (*e->deleter)(e->key(), e->value);+ free(e);+ }+}++void LRUCache::LRU_Remove(LRUHandle* e) {+ e->next->prev = e->prev;+ e->prev->next = e->next;+}++void LRUCache::LRU_Append(LRUHandle* e) {+ // Make "e" newest entry by inserting just before lru_+ e->next = &lru_;+ e->prev = lru_.prev;+ e->prev->next = e;+ e->next->prev = e;+}++Cache::Handle* LRUCache::Lookup(const Slice& key, uint32_t hash) {+ MutexLock l(&mutex_);+ LRUHandle* e = table_.Lookup(key, hash);+ if (e != NULL) {+ e->refs++;+ LRU_Remove(e);+ LRU_Append(e);+ }+ return reinterpret_cast<Cache::Handle*>(e);+}++void LRUCache::Release(Cache::Handle* handle) {+ MutexLock l(&mutex_);+ Unref(reinterpret_cast<LRUHandle*>(handle));+}++Cache::Handle* LRUCache::Insert(+ const Slice& key, uint32_t hash, void* value, size_t charge,+ void (*deleter)(const Slice& key, void* value)) {+ MutexLock l(&mutex_);++ LRUHandle* e = reinterpret_cast<LRUHandle*>(+ malloc(sizeof(LRUHandle)-1 + key.size()));+ e->value = value;+ e->deleter = deleter;+ e->charge = charge;+ e->key_length = key.size();+ e->hash = hash;+ e->refs = 2; // One from LRUCache, one for the returned handle+ memcpy(e->key_data, key.data(), key.size());+ LRU_Append(e);+ usage_ += charge;++ LRUHandle* old = table_.Insert(e);+ if (old != NULL) {+ LRU_Remove(old);+ Unref(old);+ }++ while (usage_ > capacity_ && lru_.next != &lru_) {+ LRUHandle* old = lru_.next;+ LRU_Remove(old);+ table_.Remove(old->key(), old->hash);+ Unref(old);+ }++ return reinterpret_cast<Cache::Handle*>(e);+}++void LRUCache::Erase(const Slice& key, uint32_t hash) {+ MutexLock l(&mutex_);+ LRUHandle* e = table_.Remove(key, hash);+ if (e != NULL) {+ LRU_Remove(e);+ Unref(e);+ }+}++static const int kNumShardBits = 4;+static const int kNumShards = 1 << kNumShardBits;++class ShardedLRUCache : public Cache {+ private:+ LRUCache shard_[kNumShards];+ port::Mutex id_mutex_;+ uint64_t last_id_;++ static inline uint32_t HashSlice(const Slice& s) {+ return Hash(s.data(), s.size(), 0);+ }++ static uint32_t Shard(uint32_t hash) {+ return hash >> (32 - kNumShardBits);+ }++ public:+ explicit ShardedLRUCache(size_t capacity)+ : last_id_(0) {+ const size_t per_shard = (capacity + (kNumShards - 1)) / kNumShards;+ for (int s = 0; s < kNumShards; s++) {+ shard_[s].SetCapacity(per_shard);+ }+ }+ virtual ~ShardedLRUCache() { }+ virtual Handle* Insert(const Slice& key, void* value, size_t charge,+ void (*deleter)(const Slice& key, void* value)) {+ const uint32_t hash = HashSlice(key);+ return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);+ }+ virtual Handle* Lookup(const Slice& key) {+ const uint32_t hash = HashSlice(key);+ return shard_[Shard(hash)].Lookup(key, hash);+ }+ virtual void Release(Handle* handle) {+ LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);+ shard_[Shard(h->hash)].Release(handle);+ }+ virtual void Erase(const Slice& key) {+ const uint32_t hash = HashSlice(key);+ shard_[Shard(hash)].Erase(key, hash);+ }+ virtual void* Value(Handle* handle) {+ return reinterpret_cast<LRUHandle*>(handle)->value;+ }+ virtual uint64_t NewId() {+ MutexLock l(&id_mutex_);+ return ++(last_id_);+ }+};++} // end anonymous namespace++Cache* NewLRUCache(size_t capacity) {+ return new ShardedLRUCache(capacity);+}++} // namespace leveldb
+ cbits/leveldb/util/cache_test.cc view
@@ -0,0 +1,186 @@+// 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/cache.h"++#include <vector>+#include "util/coding.h"+#include "util/testharness.h"++namespace leveldb {++// Conversions between numeric keys/values and the types expected by Cache.+static std::string EncodeKey(int k) {+ std::string result;+ PutFixed32(&result, k);+ return result;+}+static int DecodeKey(const Slice& k) {+ assert(k.size() == 4);+ return DecodeFixed32(k.data());+}+static void* EncodeValue(uintptr_t v) { return reinterpret_cast<void*>(v); }+static int DecodeValue(void* v) { return reinterpret_cast<uintptr_t>(v); }++class CacheTest {+ public:+ static CacheTest* current_;++ static void Deleter(const Slice& key, void* v) {+ current_->deleted_keys_.push_back(DecodeKey(key));+ current_->deleted_values_.push_back(DecodeValue(v));+ }++ static const int kCacheSize = 1000;+ std::vector<int> deleted_keys_;+ std::vector<int> deleted_values_;+ Cache* cache_;++ CacheTest() : cache_(NewLRUCache(kCacheSize)) {+ current_ = this;+ }++ ~CacheTest() {+ delete cache_;+ }++ int Lookup(int key) {+ Cache::Handle* handle = cache_->Lookup(EncodeKey(key));+ const int r = (handle == NULL) ? -1 : DecodeValue(cache_->Value(handle));+ if (handle != NULL) {+ cache_->Release(handle);+ }+ return r;+ }++ void Insert(int key, int value, int charge = 1) {+ cache_->Release(cache_->Insert(EncodeKey(key), EncodeValue(value), charge,+ &CacheTest::Deleter));+ }++ void Erase(int key) {+ cache_->Erase(EncodeKey(key));+ }+};+CacheTest* CacheTest::current_;++TEST(CacheTest, HitAndMiss) {+ ASSERT_EQ(-1, Lookup(100));++ Insert(100, 101);+ ASSERT_EQ(101, Lookup(100));+ ASSERT_EQ(-1, Lookup(200));+ ASSERT_EQ(-1, Lookup(300));++ Insert(200, 201);+ ASSERT_EQ(101, Lookup(100));+ ASSERT_EQ(201, Lookup(200));+ ASSERT_EQ(-1, Lookup(300));++ Insert(100, 102);+ ASSERT_EQ(102, Lookup(100));+ ASSERT_EQ(201, Lookup(200));+ ASSERT_EQ(-1, Lookup(300));++ ASSERT_EQ(1, deleted_keys_.size());+ ASSERT_EQ(100, deleted_keys_[0]);+ ASSERT_EQ(101, deleted_values_[0]);+}++TEST(CacheTest, Erase) {+ Erase(200);+ ASSERT_EQ(0, deleted_keys_.size());++ Insert(100, 101);+ Insert(200, 201);+ Erase(100);+ ASSERT_EQ(-1, Lookup(100));+ ASSERT_EQ(201, Lookup(200));+ ASSERT_EQ(1, deleted_keys_.size());+ ASSERT_EQ(100, deleted_keys_[0]);+ ASSERT_EQ(101, deleted_values_[0]);++ Erase(100);+ ASSERT_EQ(-1, Lookup(100));+ ASSERT_EQ(201, Lookup(200));+ ASSERT_EQ(1, deleted_keys_.size());+}++TEST(CacheTest, EntriesArePinned) {+ Insert(100, 101);+ Cache::Handle* h1 = cache_->Lookup(EncodeKey(100));+ ASSERT_EQ(101, DecodeValue(cache_->Value(h1)));++ Insert(100, 102);+ Cache::Handle* h2 = cache_->Lookup(EncodeKey(100));+ ASSERT_EQ(102, DecodeValue(cache_->Value(h2)));+ ASSERT_EQ(0, deleted_keys_.size());++ cache_->Release(h1);+ ASSERT_EQ(1, deleted_keys_.size());+ ASSERT_EQ(100, deleted_keys_[0]);+ ASSERT_EQ(101, deleted_values_[0]);++ Erase(100);+ ASSERT_EQ(-1, Lookup(100));+ ASSERT_EQ(1, deleted_keys_.size());++ cache_->Release(h2);+ ASSERT_EQ(2, deleted_keys_.size());+ ASSERT_EQ(100, deleted_keys_[1]);+ ASSERT_EQ(102, deleted_values_[1]);+}++TEST(CacheTest, EvictionPolicy) {+ Insert(100, 101);+ Insert(200, 201);++ // Frequently used entry must be kept around+ for (int i = 0; i < kCacheSize + 100; i++) {+ Insert(1000+i, 2000+i);+ ASSERT_EQ(2000+i, Lookup(1000+i));+ ASSERT_EQ(101, Lookup(100));+ }+ ASSERT_EQ(101, Lookup(100));+ ASSERT_EQ(-1, Lookup(200));+}++TEST(CacheTest, HeavyEntries) {+ // Add a bunch of light and heavy entries and then count the combined+ // size of items still in the cache, which must be approximately the+ // same as the total capacity.+ const int kLight = 1;+ const int kHeavy = 10;+ int added = 0;+ int index = 0;+ while (added < 2*kCacheSize) {+ const int weight = (index & 1) ? kLight : kHeavy;+ Insert(index, 1000+index, weight);+ added += weight;+ index++;+ }++ int cached_weight = 0;+ for (int i = 0; i < index; i++) {+ const int weight = (i & 1 ? kLight : kHeavy);+ int r = Lookup(i);+ if (r >= 0) {+ cached_weight += weight;+ ASSERT_EQ(1000+i, r);+ }+ }+ ASSERT_LE(cached_weight, kCacheSize + kCacheSize/10);+}++TEST(CacheTest, NewId) {+ uint64_t a = cache_->NewId();+ uint64_t b = cache_->NewId();+ ASSERT_NE(a, b);+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/util/coding.cc view
@@ -0,0 +1,194 @@+// 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 "util/coding.h"++namespace leveldb {++void EncodeFixed32(char* buf, uint32_t value) {+#if __BYTE_ORDER == __LITTLE_ENDIAN+ memcpy(buf, &value, sizeof(value));+#else+ buf[0] = value & 0xff;+ buf[1] = (value >> 8) & 0xff;+ buf[2] = (value >> 16) & 0xff;+ buf[3] = (value >> 24) & 0xff;+#endif+}++void EncodeFixed64(char* buf, uint64_t value) {+#if __BYTE_ORDER == __LITTLE_ENDIAN+ memcpy(buf, &value, sizeof(value));+#else+ buf[0] = value & 0xff;+ buf[1] = (value >> 8) & 0xff;+ buf[2] = (value >> 16) & 0xff;+ buf[3] = (value >> 24) & 0xff;+ buf[4] = (value >> 32) & 0xff;+ buf[5] = (value >> 40) & 0xff;+ buf[6] = (value >> 48) & 0xff;+ buf[7] = (value >> 56) & 0xff;+#endif+}++void PutFixed32(std::string* dst, uint32_t value) {+ char buf[sizeof(value)];+ EncodeFixed32(buf, value);+ dst->append(buf, sizeof(buf));+}++void PutFixed64(std::string* dst, uint64_t value) {+ char buf[sizeof(value)];+ EncodeFixed64(buf, value);+ dst->append(buf, sizeof(buf));+}++char* EncodeVarint32(char* dst, uint32_t v) {+ // Operate on characters as unsigneds+ unsigned char* ptr = reinterpret_cast<unsigned char*>(dst);+ static const int B = 128;+ if (v < (1<<7)) {+ *(ptr++) = v;+ } else if (v < (1<<14)) {+ *(ptr++) = v | B;+ *(ptr++) = v>>7;+ } else if (v < (1<<21)) {+ *(ptr++) = v | B;+ *(ptr++) = (v>>7) | B;+ *(ptr++) = v>>14;+ } else if (v < (1<<28)) {+ *(ptr++) = v | B;+ *(ptr++) = (v>>7) | B;+ *(ptr++) = (v>>14) | B;+ *(ptr++) = v>>21;+ } else {+ *(ptr++) = v | B;+ *(ptr++) = (v>>7) | B;+ *(ptr++) = (v>>14) | B;+ *(ptr++) = (v>>21) | B;+ *(ptr++) = v>>28;+ }+ return reinterpret_cast<char*>(ptr);+}++void PutVarint32(std::string* dst, uint32_t v) {+ char buf[5];+ char* ptr = EncodeVarint32(buf, v);+ dst->append(buf, ptr - buf);+}++char* EncodeVarint64(char* dst, uint64_t v) {+ static const int B = 128;+ unsigned char* ptr = reinterpret_cast<unsigned char*>(dst);+ while (v >= B) {+ *(ptr++) = (v & (B-1)) | B;+ v >>= 7;+ }+ *(ptr++) = static_cast<unsigned char>(v);+ return reinterpret_cast<char*>(ptr);+}++void PutVarint64(std::string* dst, uint64_t v) {+ char buf[10];+ char* ptr = EncodeVarint64(buf, v);+ dst->append(buf, ptr - buf);+}++void PutLengthPrefixedSlice(std::string* dst, const Slice& value) {+ PutVarint32(dst, value.size());+ dst->append(value.data(), value.size());+}++int VarintLength(uint64_t v) {+ int len = 1;+ while (v >= 128) {+ v >>= 7;+ len++;+ }+ return len;+}++const char* GetVarint32PtrFallback(const char* p,+ const char* limit,+ uint32_t* value) {+ uint32_t result = 0;+ for (uint32_t shift = 0; shift <= 28 && p < limit; shift += 7) {+ uint32_t byte = *(reinterpret_cast<const unsigned char*>(p));+ p++;+ if (byte & 128) {+ // More bytes are present+ result |= ((byte & 127) << shift);+ } else {+ result |= (byte << shift);+ *value = result;+ return reinterpret_cast<const char*>(p);+ }+ }+ return NULL;+}++bool GetVarint32(Slice* input, uint32_t* value) {+ const char* p = input->data();+ const char* limit = p + input->size();+ const char* q = GetVarint32Ptr(p, limit, value);+ if (q == NULL) {+ return false;+ } else {+ *input = Slice(q, limit - q);+ return true;+ }+}++const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* value) {+ uint64_t result = 0;+ for (uint32_t shift = 0; shift <= 63 && p < limit; shift += 7) {+ uint64_t byte = *(reinterpret_cast<const unsigned char*>(p));+ p++;+ if (byte & 128) {+ // More bytes are present+ result |= ((byte & 127) << shift);+ } else {+ result |= (byte << shift);+ *value = result;+ return reinterpret_cast<const char*>(p);+ }+ }+ return NULL;+}++bool GetVarint64(Slice* input, uint64_t* value) {+ const char* p = input->data();+ const char* limit = p + input->size();+ const char* q = GetVarint64Ptr(p, limit, value);+ if (q == NULL) {+ return false;+ } else {+ *input = Slice(q, limit - q);+ return true;+ }+}++const char* GetLengthPrefixedSlice(const char* p, const char* limit,+ Slice* result) {+ uint32_t len;+ p = GetVarint32Ptr(p, limit, &len);+ if (p == NULL) return NULL;+ if (p + len > limit) return NULL;+ *result = Slice(p, len);+ return p + len;+}++bool GetLengthPrefixedSlice(Slice* input, Slice* result) {+ uint32_t len;+ if (GetVarint32(input, &len) &&+ input->size() >= len) {+ *result = Slice(input->data(), len);+ input->remove_prefix(len);+ return true;+ } else {+ return false;+ }+}++} // namespace leveldb
+ cbits/leveldb/util/coding.h view
@@ -0,0 +1,104 @@+// 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.+//+// Endian-neutral encoding:+// * Fixed-length numbers are encoded with least-significant byte first+// * In addition we support variable length "varint" encoding+// * Strings are encoded prefixed by their length in varint format++#ifndef STORAGE_LEVELDB_UTIL_CODING_H_+#define STORAGE_LEVELDB_UTIL_CODING_H_++#include <stdint.h>+#include <string.h>+#include <string>+#include "leveldb/slice.h"+#include "port/port.h"++namespace leveldb {++// Standard Put... routines append to a string+extern void PutFixed32(std::string* dst, uint32_t value);+extern void PutFixed64(std::string* dst, uint64_t value);+extern void PutVarint32(std::string* dst, uint32_t value);+extern void PutVarint64(std::string* dst, uint64_t value);+extern void PutLengthPrefixedSlice(std::string* dst, const Slice& value);++// Standard Get... routines parse a value from the beginning of a Slice+// and advance the slice past the parsed value.+extern bool GetVarint32(Slice* input, uint32_t* value);+extern bool GetVarint64(Slice* input, uint64_t* value);+extern bool GetLengthPrefixedSlice(Slice* input, Slice* result);++// Pointer-based variants of GetVarint... These either store a value+// in *v and return a pointer just past the parsed value, or return+// NULL on error. These routines only look at bytes in the range+// [p..limit-1]+extern const char* GetVarint32Ptr(const char* p,const char* limit, uint32_t* v);+extern const char* GetVarint64Ptr(const char* p,const char* limit, uint64_t* v);++// Returns the length of the varint32 or varint64 encoding of "v"+extern int VarintLength(uint64_t v);++// Lower-level versions of Put... that write directly into a character buffer+// REQUIRES: dst has enough space for the value being written+extern void EncodeFixed32(char* dst, uint32_t value);+extern void EncodeFixed64(char* dst, uint64_t value);++// Lower-level versions of Put... that write directly into a character buffer+// and return a pointer just past the last byte written.+// REQUIRES: dst has enough space for the value being written+extern char* EncodeVarint32(char* dst, uint32_t value);+extern char* EncodeVarint64(char* dst, uint64_t value);++// Lower-level versions of Get... that read directly from a character buffer+// without any bounds checking.++inline uint32_t DecodeFixed32(const char* ptr) {+ if (port::kLittleEndian) {+ // Load the raw bytes+ uint32_t result;+ memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load+ return result;+ } else {+ return ((static_cast<uint32_t>(static_cast<unsigned char>(ptr[0])))+ | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[1])) << 8)+ | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[2])) << 16)+ | (static_cast<uint32_t>(static_cast<unsigned char>(ptr[3])) << 24));+ }+}++inline uint64_t DecodeFixed64(const char* ptr) {+ if (port::kLittleEndian) {+ // Load the raw bytes+ uint64_t result;+ memcpy(&result, ptr, sizeof(result)); // gcc optimizes this to a plain load+ return result;+ } else {+ uint64_t lo = DecodeFixed32(ptr);+ uint64_t hi = DecodeFixed32(ptr + 4);+ return (hi << 32) | lo;+ }+}++// Internal routine for use by fallback path of GetVarint32Ptr+extern const char* GetVarint32PtrFallback(const char* p,+ const char* limit,+ uint32_t* value);+inline const char* GetVarint32Ptr(const char* p,+ const char* limit,+ uint32_t* value) {+ if (p < limit) {+ uint32_t result = *(reinterpret_cast<const unsigned char*>(p));+ if ((result & 128) == 0) {+ *value = result;+ return p + 1;+ }+ }+ return GetVarint32PtrFallback(p, limit, value);+}++} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_CODING_H_
+ cbits/leveldb/util/coding_test.cc view
@@ -0,0 +1,173 @@+// 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 "util/coding.h"++#include "util/testharness.h"++namespace leveldb {++class Coding { };++TEST(Coding, Fixed32) {+ std::string s;+ for (uint32_t v = 0; v < 100000; v++) {+ PutFixed32(&s, v);+ }++ const char* p = s.data();+ for (uint32_t v = 0; v < 100000; v++) {+ uint32_t actual = DecodeFixed32(p);+ ASSERT_EQ(v, actual);+ p += sizeof(uint32_t);+ }+}++TEST(Coding, Fixed64) {+ std::string s;+ for (int power = 0; power <= 63; power++) {+ uint64_t v = static_cast<uint64_t>(1) << power;+ PutFixed64(&s, v - 1);+ PutFixed64(&s, v + 0);+ PutFixed64(&s, v + 1);+ }++ const char* p = s.data();+ for (int power = 0; power <= 63; power++) {+ uint64_t v = static_cast<uint64_t>(1) << power;+ uint64_t actual;+ actual = DecodeFixed64(p);+ ASSERT_EQ(v-1, actual);+ p += sizeof(uint64_t);++ actual = DecodeFixed64(p);+ ASSERT_EQ(v+0, actual);+ p += sizeof(uint64_t);++ actual = DecodeFixed64(p);+ ASSERT_EQ(v+1, actual);+ p += sizeof(uint64_t);+ }+}++TEST(Coding, Varint32) {+ std::string s;+ for (uint32_t i = 0; i < (32 * 32); i++) {+ uint32_t v = (i / 32) << (i % 32);+ PutVarint32(&s, v);+ }++ const char* p = s.data();+ const char* limit = p + s.size();+ for (uint32_t i = 0; i < (32 * 32); i++) {+ uint32_t expected = (i / 32) << (i % 32);+ uint32_t actual;+ const char* start = p;+ p = GetVarint32Ptr(p, limit, &actual);+ ASSERT_TRUE(p != NULL);+ ASSERT_EQ(expected, actual);+ ASSERT_EQ(VarintLength(actual), p - start);+ }+ ASSERT_EQ(p, s.data() + s.size());+}++TEST(Coding, Varint64) {+ // Construct the list of values to check+ std::vector<uint64_t> values;+ // Some special values+ values.push_back(0);+ values.push_back(100);+ values.push_back(~static_cast<uint64_t>(0));+ values.push_back(~static_cast<uint64_t>(0) - 1);+ for (uint32_t k = 0; k < 64; k++) {+ // Test values near powers of two+ const uint64_t power = 1ull << k;+ values.push_back(power);+ values.push_back(power-1);+ values.push_back(power+1);+ };++ std::string s;+ for (int i = 0; i < values.size(); i++) {+ PutVarint64(&s, values[i]);+ }++ const char* p = s.data();+ const char* limit = p + s.size();+ for (int i = 0; i < values.size(); i++) {+ ASSERT_TRUE(p < limit);+ uint64_t actual;+ const char* start = p;+ p = GetVarint64Ptr(p, limit, &actual);+ ASSERT_TRUE(p != NULL);+ ASSERT_EQ(values[i], actual);+ ASSERT_EQ(VarintLength(actual), p - start);+ }+ ASSERT_EQ(p, limit);++}++TEST(Coding, Varint32Overflow) {+ uint32_t result;+ std::string input("\x81\x82\x83\x84\x85\x11");+ ASSERT_TRUE(GetVarint32Ptr(input.data(), input.data() + input.size(), &result)+ == NULL);+}++TEST(Coding, Varint32Truncation) {+ uint32_t large_value = (1u << 31) + 100;+ std::string s;+ PutVarint32(&s, large_value);+ uint32_t result;+ for (int len = 0; len < s.size() - 1; len++) {+ ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + len, &result) == NULL);+ }+ ASSERT_TRUE(GetVarint32Ptr(s.data(), s.data() + s.size(), &result) != NULL);+ ASSERT_EQ(large_value, result);+}++TEST(Coding, Varint64Overflow) {+ uint64_t result;+ std::string input("\x81\x82\x83\x84\x85\x81\x82\x83\x84\x85\x11");+ ASSERT_TRUE(GetVarint64Ptr(input.data(), input.data() + input.size(), &result)+ == NULL);+}++TEST(Coding, Varint64Truncation) {+ uint64_t large_value = (1ull << 63) + 100ull;+ std::string s;+ PutVarint64(&s, large_value);+ uint64_t result;+ for (int len = 0; len < s.size() - 1; len++) {+ ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + len, &result) == NULL);+ }+ ASSERT_TRUE(GetVarint64Ptr(s.data(), s.data() + s.size(), &result) != NULL);+ ASSERT_EQ(large_value, result);+}++TEST(Coding, Strings) {+ std::string s;+ PutLengthPrefixedSlice(&s, Slice(""));+ PutLengthPrefixedSlice(&s, Slice("foo"));+ PutLengthPrefixedSlice(&s, Slice("bar"));+ PutLengthPrefixedSlice(&s, Slice(std::string(200, 'x')));++ Slice input(s);+ Slice v;+ ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v));+ ASSERT_EQ("", v.ToString());+ ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v));+ ASSERT_EQ("foo", v.ToString());+ ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v));+ ASSERT_EQ("bar", v.ToString());+ ASSERT_TRUE(GetLengthPrefixedSlice(&input, &v));+ ASSERT_EQ(std::string(200, 'x'), v.ToString());+ ASSERT_EQ("", input.ToString());+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/util/comparator.cc view
@@ -0,0 +1,76 @@+// 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 <algorithm>+#include <stdint.h>+#include "leveldb/comparator.h"+#include "leveldb/slice.h"+#include "util/logging.h"++namespace leveldb {++Comparator::~Comparator() { }++namespace {+class BytewiseComparatorImpl : public Comparator {+ public:+ BytewiseComparatorImpl() { }++ virtual const char* Name() const {+ return "leveldb.BytewiseComparator";+ }++ virtual int Compare(const Slice& a, const Slice& b) const {+ return a.compare(b);+ }++ virtual void FindShortestSeparator(+ std::string* start,+ const Slice& limit) const {+ // Find length of common prefix+ size_t min_length = std::min(start->size(), limit.size());+ size_t diff_index = 0;+ while ((diff_index < min_length) &&+ ((*start)[diff_index] == limit[diff_index])) {+ diff_index++;+ }++ if (diff_index >= min_length) {+ // Do not shorten if one string is a prefix of the other+ } else {+ uint8_t diff_byte = static_cast<uint8_t>((*start)[diff_index]);+ if (diff_byte < static_cast<uint8_t>(0xff) &&+ diff_byte + 1 < static_cast<uint8_t>(limit[diff_index])) {+ (*start)[diff_index]++;+ start->resize(diff_index + 1);+ assert(Compare(*start, limit) < 0);+ }+ }+ }++ virtual void FindShortSuccessor(std::string* key) const {+ // Find first character that can be incremented+ size_t n = key->size();+ for (size_t i = 0; i < n; i++) {+ const uint8_t byte = (*key)[i];+ if (byte != static_cast<uint8_t>(0xff)) {+ (*key)[i] = byte + 1;+ key->resize(i+1);+ return;+ }+ }+ // *key is a run of 0xffs. Leave it alone.+ }+};+} // namespace++// Intentionally not destroyed to prevent destructor racing+// with background threads.+static const Comparator* bytewise = new BytewiseComparatorImpl;++const Comparator* BytewiseComparator() {+ return bytewise;+}++} // namespace leveldb
+ cbits/leveldb/util/crc32c.cc view
@@ -0,0 +1,332 @@+// 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.+//+// A portable implementation of crc32c, optimized to handle+// four bytes at a time.++#include "util/crc32c.h"++#include <stdint.h>+#include "util/coding.h"++namespace leveldb {+namespace crc32c {++static const uint32_t table0_[256] = {+ 0x00000000, 0xf26b8303, 0xe13b70f7, 0x1350f3f4,+ 0xc79a971f, 0x35f1141c, 0x26a1e7e8, 0xd4ca64eb,+ 0x8ad958cf, 0x78b2dbcc, 0x6be22838, 0x9989ab3b,+ 0x4d43cfd0, 0xbf284cd3, 0xac78bf27, 0x5e133c24,+ 0x105ec76f, 0xe235446c, 0xf165b798, 0x030e349b,+ 0xd7c45070, 0x25afd373, 0x36ff2087, 0xc494a384,+ 0x9a879fa0, 0x68ec1ca3, 0x7bbcef57, 0x89d76c54,+ 0x5d1d08bf, 0xaf768bbc, 0xbc267848, 0x4e4dfb4b,+ 0x20bd8ede, 0xd2d60ddd, 0xc186fe29, 0x33ed7d2a,+ 0xe72719c1, 0x154c9ac2, 0x061c6936, 0xf477ea35,+ 0xaa64d611, 0x580f5512, 0x4b5fa6e6, 0xb93425e5,+ 0x6dfe410e, 0x9f95c20d, 0x8cc531f9, 0x7eaeb2fa,+ 0x30e349b1, 0xc288cab2, 0xd1d83946, 0x23b3ba45,+ 0xf779deae, 0x05125dad, 0x1642ae59, 0xe4292d5a,+ 0xba3a117e, 0x4851927d, 0x5b016189, 0xa96ae28a,+ 0x7da08661, 0x8fcb0562, 0x9c9bf696, 0x6ef07595,+ 0x417b1dbc, 0xb3109ebf, 0xa0406d4b, 0x522bee48,+ 0x86e18aa3, 0x748a09a0, 0x67dafa54, 0x95b17957,+ 0xcba24573, 0x39c9c670, 0x2a993584, 0xd8f2b687,+ 0x0c38d26c, 0xfe53516f, 0xed03a29b, 0x1f682198,+ 0x5125dad3, 0xa34e59d0, 0xb01eaa24, 0x42752927,+ 0x96bf4dcc, 0x64d4cecf, 0x77843d3b, 0x85efbe38,+ 0xdbfc821c, 0x2997011f, 0x3ac7f2eb, 0xc8ac71e8,+ 0x1c661503, 0xee0d9600, 0xfd5d65f4, 0x0f36e6f7,+ 0x61c69362, 0x93ad1061, 0x80fde395, 0x72966096,+ 0xa65c047d, 0x5437877e, 0x4767748a, 0xb50cf789,+ 0xeb1fcbad, 0x197448ae, 0x0a24bb5a, 0xf84f3859,+ 0x2c855cb2, 0xdeeedfb1, 0xcdbe2c45, 0x3fd5af46,+ 0x7198540d, 0x83f3d70e, 0x90a324fa, 0x62c8a7f9,+ 0xb602c312, 0x44694011, 0x5739b3e5, 0xa55230e6,+ 0xfb410cc2, 0x092a8fc1, 0x1a7a7c35, 0xe811ff36,+ 0x3cdb9bdd, 0xceb018de, 0xdde0eb2a, 0x2f8b6829,+ 0x82f63b78, 0x709db87b, 0x63cd4b8f, 0x91a6c88c,+ 0x456cac67, 0xb7072f64, 0xa457dc90, 0x563c5f93,+ 0x082f63b7, 0xfa44e0b4, 0xe9141340, 0x1b7f9043,+ 0xcfb5f4a8, 0x3dde77ab, 0x2e8e845f, 0xdce5075c,+ 0x92a8fc17, 0x60c37f14, 0x73938ce0, 0x81f80fe3,+ 0x55326b08, 0xa759e80b, 0xb4091bff, 0x466298fc,+ 0x1871a4d8, 0xea1a27db, 0xf94ad42f, 0x0b21572c,+ 0xdfeb33c7, 0x2d80b0c4, 0x3ed04330, 0xccbbc033,+ 0xa24bb5a6, 0x502036a5, 0x4370c551, 0xb11b4652,+ 0x65d122b9, 0x97baa1ba, 0x84ea524e, 0x7681d14d,+ 0x2892ed69, 0xdaf96e6a, 0xc9a99d9e, 0x3bc21e9d,+ 0xef087a76, 0x1d63f975, 0x0e330a81, 0xfc588982,+ 0xb21572c9, 0x407ef1ca, 0x532e023e, 0xa145813d,+ 0x758fe5d6, 0x87e466d5, 0x94b49521, 0x66df1622,+ 0x38cc2a06, 0xcaa7a905, 0xd9f75af1, 0x2b9cd9f2,+ 0xff56bd19, 0x0d3d3e1a, 0x1e6dcdee, 0xec064eed,+ 0xc38d26c4, 0x31e6a5c7, 0x22b65633, 0xd0ddd530,+ 0x0417b1db, 0xf67c32d8, 0xe52cc12c, 0x1747422f,+ 0x49547e0b, 0xbb3ffd08, 0xa86f0efc, 0x5a048dff,+ 0x8ecee914, 0x7ca56a17, 0x6ff599e3, 0x9d9e1ae0,+ 0xd3d3e1ab, 0x21b862a8, 0x32e8915c, 0xc083125f,+ 0x144976b4, 0xe622f5b7, 0xf5720643, 0x07198540,+ 0x590ab964, 0xab613a67, 0xb831c993, 0x4a5a4a90,+ 0x9e902e7b, 0x6cfbad78, 0x7fab5e8c, 0x8dc0dd8f,+ 0xe330a81a, 0x115b2b19, 0x020bd8ed, 0xf0605bee,+ 0x24aa3f05, 0xd6c1bc06, 0xc5914ff2, 0x37faccf1,+ 0x69e9f0d5, 0x9b8273d6, 0x88d28022, 0x7ab90321,+ 0xae7367ca, 0x5c18e4c9, 0x4f48173d, 0xbd23943e,+ 0xf36e6f75, 0x0105ec76, 0x12551f82, 0xe03e9c81,+ 0x34f4f86a, 0xc69f7b69, 0xd5cf889d, 0x27a40b9e,+ 0x79b737ba, 0x8bdcb4b9, 0x988c474d, 0x6ae7c44e,+ 0xbe2da0a5, 0x4c4623a6, 0x5f16d052, 0xad7d5351+};+static const uint32_t table1_[256] = {+ 0x00000000, 0x13a29877, 0x274530ee, 0x34e7a899,+ 0x4e8a61dc, 0x5d28f9ab, 0x69cf5132, 0x7a6dc945,+ 0x9d14c3b8, 0x8eb65bcf, 0xba51f356, 0xa9f36b21,+ 0xd39ea264, 0xc03c3a13, 0xf4db928a, 0xe7790afd,+ 0x3fc5f181, 0x2c6769f6, 0x1880c16f, 0x0b225918,+ 0x714f905d, 0x62ed082a, 0x560aa0b3, 0x45a838c4,+ 0xa2d13239, 0xb173aa4e, 0x859402d7, 0x96369aa0,+ 0xec5b53e5, 0xfff9cb92, 0xcb1e630b, 0xd8bcfb7c,+ 0x7f8be302, 0x6c297b75, 0x58ced3ec, 0x4b6c4b9b,+ 0x310182de, 0x22a31aa9, 0x1644b230, 0x05e62a47,+ 0xe29f20ba, 0xf13db8cd, 0xc5da1054, 0xd6788823,+ 0xac154166, 0xbfb7d911, 0x8b507188, 0x98f2e9ff,+ 0x404e1283, 0x53ec8af4, 0x670b226d, 0x74a9ba1a,+ 0x0ec4735f, 0x1d66eb28, 0x298143b1, 0x3a23dbc6,+ 0xdd5ad13b, 0xcef8494c, 0xfa1fe1d5, 0xe9bd79a2,+ 0x93d0b0e7, 0x80722890, 0xb4958009, 0xa737187e,+ 0xff17c604, 0xecb55e73, 0xd852f6ea, 0xcbf06e9d,+ 0xb19da7d8, 0xa23f3faf, 0x96d89736, 0x857a0f41,+ 0x620305bc, 0x71a19dcb, 0x45463552, 0x56e4ad25,+ 0x2c896460, 0x3f2bfc17, 0x0bcc548e, 0x186eccf9,+ 0xc0d23785, 0xd370aff2, 0xe797076b, 0xf4359f1c,+ 0x8e585659, 0x9dface2e, 0xa91d66b7, 0xbabffec0,+ 0x5dc6f43d, 0x4e646c4a, 0x7a83c4d3, 0x69215ca4,+ 0x134c95e1, 0x00ee0d96, 0x3409a50f, 0x27ab3d78,+ 0x809c2506, 0x933ebd71, 0xa7d915e8, 0xb47b8d9f,+ 0xce1644da, 0xddb4dcad, 0xe9537434, 0xfaf1ec43,+ 0x1d88e6be, 0x0e2a7ec9, 0x3acdd650, 0x296f4e27,+ 0x53028762, 0x40a01f15, 0x7447b78c, 0x67e52ffb,+ 0xbf59d487, 0xacfb4cf0, 0x981ce469, 0x8bbe7c1e,+ 0xf1d3b55b, 0xe2712d2c, 0xd69685b5, 0xc5341dc2,+ 0x224d173f, 0x31ef8f48, 0x050827d1, 0x16aabfa6,+ 0x6cc776e3, 0x7f65ee94, 0x4b82460d, 0x5820de7a,+ 0xfbc3faf9, 0xe861628e, 0xdc86ca17, 0xcf245260,+ 0xb5499b25, 0xa6eb0352, 0x920cabcb, 0x81ae33bc,+ 0x66d73941, 0x7575a136, 0x419209af, 0x523091d8,+ 0x285d589d, 0x3bffc0ea, 0x0f186873, 0x1cbaf004,+ 0xc4060b78, 0xd7a4930f, 0xe3433b96, 0xf0e1a3e1,+ 0x8a8c6aa4, 0x992ef2d3, 0xadc95a4a, 0xbe6bc23d,+ 0x5912c8c0, 0x4ab050b7, 0x7e57f82e, 0x6df56059,+ 0x1798a91c, 0x043a316b, 0x30dd99f2, 0x237f0185,+ 0x844819fb, 0x97ea818c, 0xa30d2915, 0xb0afb162,+ 0xcac27827, 0xd960e050, 0xed8748c9, 0xfe25d0be,+ 0x195cda43, 0x0afe4234, 0x3e19eaad, 0x2dbb72da,+ 0x57d6bb9f, 0x447423e8, 0x70938b71, 0x63311306,+ 0xbb8de87a, 0xa82f700d, 0x9cc8d894, 0x8f6a40e3,+ 0xf50789a6, 0xe6a511d1, 0xd242b948, 0xc1e0213f,+ 0x26992bc2, 0x353bb3b5, 0x01dc1b2c, 0x127e835b,+ 0x68134a1e, 0x7bb1d269, 0x4f567af0, 0x5cf4e287,+ 0x04d43cfd, 0x1776a48a, 0x23910c13, 0x30339464,+ 0x4a5e5d21, 0x59fcc556, 0x6d1b6dcf, 0x7eb9f5b8,+ 0x99c0ff45, 0x8a626732, 0xbe85cfab, 0xad2757dc,+ 0xd74a9e99, 0xc4e806ee, 0xf00fae77, 0xe3ad3600,+ 0x3b11cd7c, 0x28b3550b, 0x1c54fd92, 0x0ff665e5,+ 0x759baca0, 0x663934d7, 0x52de9c4e, 0x417c0439,+ 0xa6050ec4, 0xb5a796b3, 0x81403e2a, 0x92e2a65d,+ 0xe88f6f18, 0xfb2df76f, 0xcfca5ff6, 0xdc68c781,+ 0x7b5fdfff, 0x68fd4788, 0x5c1aef11, 0x4fb87766,+ 0x35d5be23, 0x26772654, 0x12908ecd, 0x013216ba,+ 0xe64b1c47, 0xf5e98430, 0xc10e2ca9, 0xd2acb4de,+ 0xa8c17d9b, 0xbb63e5ec, 0x8f844d75, 0x9c26d502,+ 0x449a2e7e, 0x5738b609, 0x63df1e90, 0x707d86e7,+ 0x0a104fa2, 0x19b2d7d5, 0x2d557f4c, 0x3ef7e73b,+ 0xd98eedc6, 0xca2c75b1, 0xfecbdd28, 0xed69455f,+ 0x97048c1a, 0x84a6146d, 0xb041bcf4, 0xa3e32483+};+static const uint32_t table2_[256] = {+ 0x00000000, 0xa541927e, 0x4f6f520d, 0xea2ec073,+ 0x9edea41a, 0x3b9f3664, 0xd1b1f617, 0x74f06469,+ 0x38513ec5, 0x9d10acbb, 0x773e6cc8, 0xd27ffeb6,+ 0xa68f9adf, 0x03ce08a1, 0xe9e0c8d2, 0x4ca15aac,+ 0x70a27d8a, 0xd5e3eff4, 0x3fcd2f87, 0x9a8cbdf9,+ 0xee7cd990, 0x4b3d4bee, 0xa1138b9d, 0x045219e3,+ 0x48f3434f, 0xedb2d131, 0x079c1142, 0xa2dd833c,+ 0xd62de755, 0x736c752b, 0x9942b558, 0x3c032726,+ 0xe144fb14, 0x4405696a, 0xae2ba919, 0x0b6a3b67,+ 0x7f9a5f0e, 0xdadbcd70, 0x30f50d03, 0x95b49f7d,+ 0xd915c5d1, 0x7c5457af, 0x967a97dc, 0x333b05a2,+ 0x47cb61cb, 0xe28af3b5, 0x08a433c6, 0xade5a1b8,+ 0x91e6869e, 0x34a714e0, 0xde89d493, 0x7bc846ed,+ 0x0f382284, 0xaa79b0fa, 0x40577089, 0xe516e2f7,+ 0xa9b7b85b, 0x0cf62a25, 0xe6d8ea56, 0x43997828,+ 0x37691c41, 0x92288e3f, 0x78064e4c, 0xdd47dc32,+ 0xc76580d9, 0x622412a7, 0x880ad2d4, 0x2d4b40aa,+ 0x59bb24c3, 0xfcfab6bd, 0x16d476ce, 0xb395e4b0,+ 0xff34be1c, 0x5a752c62, 0xb05bec11, 0x151a7e6f,+ 0x61ea1a06, 0xc4ab8878, 0x2e85480b, 0x8bc4da75,+ 0xb7c7fd53, 0x12866f2d, 0xf8a8af5e, 0x5de93d20,+ 0x29195949, 0x8c58cb37, 0x66760b44, 0xc337993a,+ 0x8f96c396, 0x2ad751e8, 0xc0f9919b, 0x65b803e5,+ 0x1148678c, 0xb409f5f2, 0x5e273581, 0xfb66a7ff,+ 0x26217bcd, 0x8360e9b3, 0x694e29c0, 0xcc0fbbbe,+ 0xb8ffdfd7, 0x1dbe4da9, 0xf7908dda, 0x52d11fa4,+ 0x1e704508, 0xbb31d776, 0x511f1705, 0xf45e857b,+ 0x80aee112, 0x25ef736c, 0xcfc1b31f, 0x6a802161,+ 0x56830647, 0xf3c29439, 0x19ec544a, 0xbcadc634,+ 0xc85da25d, 0x6d1c3023, 0x8732f050, 0x2273622e,+ 0x6ed23882, 0xcb93aafc, 0x21bd6a8f, 0x84fcf8f1,+ 0xf00c9c98, 0x554d0ee6, 0xbf63ce95, 0x1a225ceb,+ 0x8b277743, 0x2e66e53d, 0xc448254e, 0x6109b730,+ 0x15f9d359, 0xb0b84127, 0x5a968154, 0xffd7132a,+ 0xb3764986, 0x1637dbf8, 0xfc191b8b, 0x595889f5,+ 0x2da8ed9c, 0x88e97fe2, 0x62c7bf91, 0xc7862def,+ 0xfb850ac9, 0x5ec498b7, 0xb4ea58c4, 0x11abcaba,+ 0x655baed3, 0xc01a3cad, 0x2a34fcde, 0x8f756ea0,+ 0xc3d4340c, 0x6695a672, 0x8cbb6601, 0x29faf47f,+ 0x5d0a9016, 0xf84b0268, 0x1265c21b, 0xb7245065,+ 0x6a638c57, 0xcf221e29, 0x250cde5a, 0x804d4c24,+ 0xf4bd284d, 0x51fcba33, 0xbbd27a40, 0x1e93e83e,+ 0x5232b292, 0xf77320ec, 0x1d5de09f, 0xb81c72e1,+ 0xccec1688, 0x69ad84f6, 0x83834485, 0x26c2d6fb,+ 0x1ac1f1dd, 0xbf8063a3, 0x55aea3d0, 0xf0ef31ae,+ 0x841f55c7, 0x215ec7b9, 0xcb7007ca, 0x6e3195b4,+ 0x2290cf18, 0x87d15d66, 0x6dff9d15, 0xc8be0f6b,+ 0xbc4e6b02, 0x190ff97c, 0xf321390f, 0x5660ab71,+ 0x4c42f79a, 0xe90365e4, 0x032da597, 0xa66c37e9,+ 0xd29c5380, 0x77ddc1fe, 0x9df3018d, 0x38b293f3,+ 0x7413c95f, 0xd1525b21, 0x3b7c9b52, 0x9e3d092c,+ 0xeacd6d45, 0x4f8cff3b, 0xa5a23f48, 0x00e3ad36,+ 0x3ce08a10, 0x99a1186e, 0x738fd81d, 0xd6ce4a63,+ 0xa23e2e0a, 0x077fbc74, 0xed517c07, 0x4810ee79,+ 0x04b1b4d5, 0xa1f026ab, 0x4bdee6d8, 0xee9f74a6,+ 0x9a6f10cf, 0x3f2e82b1, 0xd50042c2, 0x7041d0bc,+ 0xad060c8e, 0x08479ef0, 0xe2695e83, 0x4728ccfd,+ 0x33d8a894, 0x96993aea, 0x7cb7fa99, 0xd9f668e7,+ 0x9557324b, 0x3016a035, 0xda386046, 0x7f79f238,+ 0x0b899651, 0xaec8042f, 0x44e6c45c, 0xe1a75622,+ 0xdda47104, 0x78e5e37a, 0x92cb2309, 0x378ab177,+ 0x437ad51e, 0xe63b4760, 0x0c158713, 0xa954156d,+ 0xe5f54fc1, 0x40b4ddbf, 0xaa9a1dcc, 0x0fdb8fb2,+ 0x7b2bebdb, 0xde6a79a5, 0x3444b9d6, 0x91052ba8+};+static const uint32_t table3_[256] = {+ 0x00000000, 0xdd45aab8, 0xbf672381, 0x62228939,+ 0x7b2231f3, 0xa6679b4b, 0xc4451272, 0x1900b8ca,+ 0xf64463e6, 0x2b01c95e, 0x49234067, 0x9466eadf,+ 0x8d665215, 0x5023f8ad, 0x32017194, 0xef44db2c,+ 0xe964b13d, 0x34211b85, 0x560392bc, 0x8b463804,+ 0x924680ce, 0x4f032a76, 0x2d21a34f, 0xf06409f7,+ 0x1f20d2db, 0xc2657863, 0xa047f15a, 0x7d025be2,+ 0x6402e328, 0xb9474990, 0xdb65c0a9, 0x06206a11,+ 0xd725148b, 0x0a60be33, 0x6842370a, 0xb5079db2,+ 0xac072578, 0x71428fc0, 0x136006f9, 0xce25ac41,+ 0x2161776d, 0xfc24ddd5, 0x9e0654ec, 0x4343fe54,+ 0x5a43469e, 0x8706ec26, 0xe524651f, 0x3861cfa7,+ 0x3e41a5b6, 0xe3040f0e, 0x81268637, 0x5c632c8f,+ 0x45639445, 0x98263efd, 0xfa04b7c4, 0x27411d7c,+ 0xc805c650, 0x15406ce8, 0x7762e5d1, 0xaa274f69,+ 0xb327f7a3, 0x6e625d1b, 0x0c40d422, 0xd1057e9a,+ 0xaba65fe7, 0x76e3f55f, 0x14c17c66, 0xc984d6de,+ 0xd0846e14, 0x0dc1c4ac, 0x6fe34d95, 0xb2a6e72d,+ 0x5de23c01, 0x80a796b9, 0xe2851f80, 0x3fc0b538,+ 0x26c00df2, 0xfb85a74a, 0x99a72e73, 0x44e284cb,+ 0x42c2eeda, 0x9f874462, 0xfda5cd5b, 0x20e067e3,+ 0x39e0df29, 0xe4a57591, 0x8687fca8, 0x5bc25610,+ 0xb4868d3c, 0x69c32784, 0x0be1aebd, 0xd6a40405,+ 0xcfa4bccf, 0x12e11677, 0x70c39f4e, 0xad8635f6,+ 0x7c834b6c, 0xa1c6e1d4, 0xc3e468ed, 0x1ea1c255,+ 0x07a17a9f, 0xdae4d027, 0xb8c6591e, 0x6583f3a6,+ 0x8ac7288a, 0x57828232, 0x35a00b0b, 0xe8e5a1b3,+ 0xf1e51979, 0x2ca0b3c1, 0x4e823af8, 0x93c79040,+ 0x95e7fa51, 0x48a250e9, 0x2a80d9d0, 0xf7c57368,+ 0xeec5cba2, 0x3380611a, 0x51a2e823, 0x8ce7429b,+ 0x63a399b7, 0xbee6330f, 0xdcc4ba36, 0x0181108e,+ 0x1881a844, 0xc5c402fc, 0xa7e68bc5, 0x7aa3217d,+ 0x52a0c93f, 0x8fe56387, 0xedc7eabe, 0x30824006,+ 0x2982f8cc, 0xf4c75274, 0x96e5db4d, 0x4ba071f5,+ 0xa4e4aad9, 0x79a10061, 0x1b838958, 0xc6c623e0,+ 0xdfc69b2a, 0x02833192, 0x60a1b8ab, 0xbde41213,+ 0xbbc47802, 0x6681d2ba, 0x04a35b83, 0xd9e6f13b,+ 0xc0e649f1, 0x1da3e349, 0x7f816a70, 0xa2c4c0c8,+ 0x4d801be4, 0x90c5b15c, 0xf2e73865, 0x2fa292dd,+ 0x36a22a17, 0xebe780af, 0x89c50996, 0x5480a32e,+ 0x8585ddb4, 0x58c0770c, 0x3ae2fe35, 0xe7a7548d,+ 0xfea7ec47, 0x23e246ff, 0x41c0cfc6, 0x9c85657e,+ 0x73c1be52, 0xae8414ea, 0xcca69dd3, 0x11e3376b,+ 0x08e38fa1, 0xd5a62519, 0xb784ac20, 0x6ac10698,+ 0x6ce16c89, 0xb1a4c631, 0xd3864f08, 0x0ec3e5b0,+ 0x17c35d7a, 0xca86f7c2, 0xa8a47efb, 0x75e1d443,+ 0x9aa50f6f, 0x47e0a5d7, 0x25c22cee, 0xf8878656,+ 0xe1873e9c, 0x3cc29424, 0x5ee01d1d, 0x83a5b7a5,+ 0xf90696d8, 0x24433c60, 0x4661b559, 0x9b241fe1,+ 0x8224a72b, 0x5f610d93, 0x3d4384aa, 0xe0062e12,+ 0x0f42f53e, 0xd2075f86, 0xb025d6bf, 0x6d607c07,+ 0x7460c4cd, 0xa9256e75, 0xcb07e74c, 0x16424df4,+ 0x106227e5, 0xcd278d5d, 0xaf050464, 0x7240aedc,+ 0x6b401616, 0xb605bcae, 0xd4273597, 0x09629f2f,+ 0xe6264403, 0x3b63eebb, 0x59416782, 0x8404cd3a,+ 0x9d0475f0, 0x4041df48, 0x22635671, 0xff26fcc9,+ 0x2e238253, 0xf36628eb, 0x9144a1d2, 0x4c010b6a,+ 0x5501b3a0, 0x88441918, 0xea669021, 0x37233a99,+ 0xd867e1b5, 0x05224b0d, 0x6700c234, 0xba45688c,+ 0xa345d046, 0x7e007afe, 0x1c22f3c7, 0xc167597f,+ 0xc747336e, 0x1a0299d6, 0x782010ef, 0xa565ba57,+ 0xbc65029d, 0x6120a825, 0x0302211c, 0xde478ba4,+ 0x31035088, 0xec46fa30, 0x8e647309, 0x5321d9b1,+ 0x4a21617b, 0x9764cbc3, 0xf54642fa, 0x2803e842+};++// Used to fetch a naturally-aligned 32-bit word in little endian byte-order+static inline uint32_t LE_LOAD32(const uint8_t *p) {+ return DecodeFixed32(reinterpret_cast<const char*>(p));+}++uint32_t Extend(uint32_t crc, const char* buf, size_t size) {+ const uint8_t *p = reinterpret_cast<const uint8_t *>(buf);+ const uint8_t *e = p + size;+ uint32_t l = crc ^ 0xffffffffu;++#define STEP1 do { \+ int c = (l & 0xff) ^ *p++; \+ l = table0_[c] ^ (l >> 8); \+} while (0)+#define STEP4 do { \+ uint32_t c = l ^ LE_LOAD32(p); \+ p += 4; \+ l = table3_[c & 0xff] ^ \+ table2_[(c >> 8) & 0xff] ^ \+ table1_[(c >> 16) & 0xff] ^ \+ table0_[c >> 24]; \+} while (0)++ // Point x at first 4-byte aligned byte in string. This might be+ // just past the end of the string.+ const uintptr_t pval = reinterpret_cast<uintptr_t>(p);+ const uint8_t* x = reinterpret_cast<const uint8_t*>(((pval + 3) >> 2) << 2);+ if (x <= e) {+ // Process bytes until finished or p is 4-byte aligned+ while (p != x) {+ STEP1;+ }+ }+ // Process bytes 16 at a time+ while ((e-p) >= 16) {+ STEP4; STEP4; STEP4; STEP4;+ }+ // Process bytes 4 at a time+ while ((e-p) >= 4) {+ STEP4;+ }+ // Process the last few bytes+ while (p != e) {+ STEP1;+ }+#undef STEP4+#undef STEP1+ return l ^ 0xffffffffu;+}++} // namespace crc32c+} // namespace leveldb
+ cbits/leveldb/util/crc32c.h view
@@ -0,0 +1,45 @@+// 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.++#ifndef STORAGE_LEVELDB_UTIL_CRC32C_H_+#define STORAGE_LEVELDB_UTIL_CRC32C_H_++#include <stddef.h>+#include <stdint.h>++namespace leveldb {+namespace crc32c {++// Return the crc32c of concat(A, data[0,n-1]) where init_crc is the+// crc32c of some string A. Extend() is often used to maintain the+// crc32c of a stream of data.+extern uint32_t Extend(uint32_t init_crc, const char* data, size_t n);++// Return the crc32c of data[0,n-1]+inline uint32_t Value(const char* data, size_t n) {+ return Extend(0, data, n);+}++static const uint32_t kMaskDelta = 0xa282ead8ul;++// Return a masked representation of crc.+//+// Motivation: it is problematic to compute the CRC of a string that+// contains embedded CRCs. Therefore we recommend that CRCs stored+// somewhere (e.g., in files) should be masked before being stored.+inline uint32_t Mask(uint32_t crc) {+ // Rotate right by 15 bits and add a constant.+ return ((crc >> 15) | (crc << 17)) + kMaskDelta;+}++// Return the crc whose masked representation is masked_crc.+inline uint32_t Unmask(uint32_t masked_crc) {+ uint32_t rot = masked_crc - kMaskDelta;+ return ((rot >> 17) | (rot << 15));+}++} // namespace crc32c+} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_CRC32C_H_
+ cbits/leveldb/util/crc32c_test.cc view
@@ -0,0 +1,72 @@+// 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 "util/crc32c.h"+#include "util/testharness.h"++namespace leveldb {+namespace crc32c {++class CRC { };++TEST(CRC, StandardResults) {+ // From rfc3720 section B.4.+ char buf[32];++ memset(buf, 0, sizeof(buf));+ ASSERT_EQ(0x8a9136aa, Value(buf, sizeof(buf)));++ memset(buf, 0xff, sizeof(buf));+ ASSERT_EQ(0x62a8ab43, Value(buf, sizeof(buf)));++ for (int i = 0; i < 32; i++) {+ buf[i] = i;+ }+ ASSERT_EQ(0x46dd794e, Value(buf, sizeof(buf)));++ for (int i = 0; i < 32; i++) {+ buf[i] = 31 - i;+ }+ ASSERT_EQ(0x113fdb5c, Value(buf, sizeof(buf)));++ unsigned char data[48] = {+ 0x01, 0xc0, 0x00, 0x00,+ 0x00, 0x00, 0x00, 0x00,+ 0x00, 0x00, 0x00, 0x00,+ 0x00, 0x00, 0x00, 0x00,+ 0x14, 0x00, 0x00, 0x00,+ 0x00, 0x00, 0x04, 0x00,+ 0x00, 0x00, 0x00, 0x14,+ 0x00, 0x00, 0x00, 0x18,+ 0x28, 0x00, 0x00, 0x00,+ 0x00, 0x00, 0x00, 0x00,+ 0x02, 0x00, 0x00, 0x00,+ 0x00, 0x00, 0x00, 0x00,+ };+ ASSERT_EQ(0xd9963a56, Value(reinterpret_cast<char*>(data), sizeof(data)));+}++TEST(CRC, Values) {+ ASSERT_NE(Value("a", 1), Value("foo", 3));+}++TEST(CRC, Extend) {+ ASSERT_EQ(Value("hello world", 11),+ Extend(Value("hello ", 6), "world", 5));+}++TEST(CRC, Mask) {+ uint32_t crc = Value("foo", 3);+ ASSERT_NE(crc, Mask(crc));+ ASSERT_NE(crc, Mask(Mask(crc)));+ ASSERT_EQ(crc, Unmask(Mask(crc)));+ ASSERT_EQ(crc, Unmask(Unmask(Mask(Mask(crc)))));+}++} // namespace crc32c+} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/util/env.cc view
@@ -0,0 +1,96 @@+// 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/env.h"++namespace leveldb {++Env::~Env() {+}++SequentialFile::~SequentialFile() {+}++RandomAccessFile::~RandomAccessFile() {+}++WritableFile::~WritableFile() {+}++Logger::~Logger() {+}++FileLock::~FileLock() {+}++void Log(Logger* info_log, const char* format, ...) {+ if (info_log != NULL) {+ va_list ap;+ va_start(ap, format);+ info_log->Logv(format, ap);+ va_end(ap);+ }+}++static Status DoWriteStringToFile(Env* env, const Slice& data,+ const std::string& fname,+ bool should_sync) {+ WritableFile* file;+ Status s = env->NewWritableFile(fname, &file);+ if (!s.ok()) {+ return s;+ }+ s = file->Append(data);+ if (s.ok() && should_sync) {+ s = file->Sync();+ }+ if (s.ok()) {+ s = file->Close();+ }+ delete file; // Will auto-close if we did not close above+ if (!s.ok()) {+ env->DeleteFile(fname);+ }+ return s;+}++Status WriteStringToFile(Env* env, const Slice& data,+ const std::string& fname) {+ return DoWriteStringToFile(env, data, fname, false);+}++Status WriteStringToFileSync(Env* env, const Slice& data,+ const std::string& fname) {+ return DoWriteStringToFile(env, data, fname, true);+}++Status ReadFileToString(Env* env, const std::string& fname, std::string* data) {+ data->clear();+ SequentialFile* file;+ Status s = env->NewSequentialFile(fname, &file);+ if (!s.ok()) {+ return s;+ }+ static const int kBufferSize = 8192;+ char* space = new char[kBufferSize];+ while (true) {+ Slice fragment;+ s = file->Read(kBufferSize, &fragment, space);+ if (!s.ok()) {+ break;+ }+ data->append(fragment.data(), fragment.size());+ if (fragment.empty()) {+ break;+ }+ }+ delete[] space;+ delete file;+ return s;+}++EnvWrapper::~EnvWrapper() {+}++} // namespace leveldb
+ cbits/leveldb/util/env_posix.cc view
@@ -0,0 +1,567 @@+// 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 <deque>+#include <dirent.h>+#include <errno.h>+#include <fcntl.h>+#include <pthread.h>+#include <stdio.h>+#include <stdlib.h>+#include <string.h>+#include <sys/mman.h>+#include <sys/stat.h>+#include <sys/time.h>+#include <sys/types.h>+#include <time.h>+#include <unistd.h>+#if defined(LEVELDB_PLATFORM_ANDROID)+#include <sys/stat.h>+#endif+#include "leveldb/env.h"+#include "leveldb/slice.h"+#include "port/port.h"+#include "util/logging.h"+#include "util/posix_logger.h"++namespace leveldb {++namespace {++static Status IOError(const std::string& context, int err_number) {+ return Status::IOError(context, strerror(err_number));+}++class PosixSequentialFile: public SequentialFile {+ private:+ std::string filename_;+ FILE* file_;++ public:+ PosixSequentialFile(const std::string& fname, FILE* f)+ : filename_(fname), file_(f) { }+ virtual ~PosixSequentialFile() { fclose(file_); }++ virtual Status Read(size_t n, Slice* result, char* scratch) {+ Status s;+ size_t r = fread_unlocked(scratch, 1, n, file_);+ *result = Slice(scratch, r);+ if (r < n) {+ if (feof(file_)) {+ // We leave status as ok if we hit the end of the file+ } else {+ // A partial read with an error: return a non-ok status+ s = IOError(filename_, errno);+ }+ }+ return s;+ }++ virtual Status Skip(uint64_t n) {+ if (fseek(file_, n, SEEK_CUR)) {+ return IOError(filename_, errno);+ }+ return Status::OK();+ }+};++class PosixRandomAccessFile: public RandomAccessFile {+ private:+ std::string filename_;+ int fd_;++ public:+ PosixRandomAccessFile(const std::string& fname, int fd)+ : filename_(fname), fd_(fd) { }+ virtual ~PosixRandomAccessFile() { close(fd_); }++ virtual Status Read(uint64_t offset, size_t n, Slice* result,+ char* scratch) const {+ Status s;+ ssize_t r = pread(fd_, scratch, n, static_cast<off_t>(offset));+ *result = Slice(scratch, (r < 0) ? 0 : r);+ if (r < 0) {+ // An error: return a non-ok status+ s = IOError(filename_, errno);+ }+ return s;+ }+};++// We preallocate up to an extra megabyte and use memcpy to append new+// data to the file. This is safe since we either properly close the+// file before reading from it, or for log files, the reading code+// knows enough to skip zero suffixes.+class PosixMmapFile : public WritableFile {+ private:+ std::string filename_;+ int fd_;+ size_t page_size_;+ size_t map_size_; // How much extra memory to map at a time+ char* base_; // The mapped region+ char* limit_; // Limit of the mapped region+ char* dst_; // Where to write next (in range [base_,limit_])+ char* last_sync_; // Where have we synced up to+ uint64_t file_offset_; // Offset of base_ in file++ // Have we done an munmap of unsynced data?+ bool pending_sync_;++ // Roundup x to a multiple of y+ static size_t Roundup(size_t x, size_t y) {+ return ((x + y - 1) / y) * y;+ }++ size_t TruncateToPageBoundary(size_t s) {+ s -= (s & (page_size_ - 1));+ assert((s % page_size_) == 0);+ return s;+ }++ bool UnmapCurrentRegion() {+ bool result = true;+ if (base_ != NULL) {+ if (last_sync_ < limit_) {+ // Defer syncing this data until next Sync() call, if any+ pending_sync_ = true;+ }+ if (munmap(base_, limit_ - base_) != 0) {+ result = false;+ }+ file_offset_ += limit_ - base_;+ base_ = NULL;+ limit_ = NULL;+ last_sync_ = NULL;+ dst_ = NULL;++ // Increase the amount we map the next time, but capped at 1MB+ if (map_size_ < (1<<20)) {+ map_size_ *= 2;+ }+ }+ return result;+ }++ bool MapNewRegion() {+ assert(base_ == NULL);+ if (ftruncate(fd_, file_offset_ + map_size_) < 0) {+ return false;+ }+ void* ptr = mmap(NULL, map_size_, PROT_READ | PROT_WRITE, MAP_SHARED,+ fd_, file_offset_);+ if (ptr == MAP_FAILED) {+ return false;+ }+ base_ = reinterpret_cast<char*>(ptr);+ limit_ = base_ + map_size_;+ dst_ = base_;+ last_sync_ = base_;+ return true;+ }++ public:+ PosixMmapFile(const std::string& fname, int fd, size_t page_size)+ : filename_(fname),+ fd_(fd),+ page_size_(page_size),+ map_size_(Roundup(65536, page_size)),+ base_(NULL),+ limit_(NULL),+ dst_(NULL),+ last_sync_(NULL),+ file_offset_(0),+ pending_sync_(false) {+ assert((page_size & (page_size - 1)) == 0);+ }+++ ~PosixMmapFile() {+ if (fd_ >= 0) {+ PosixMmapFile::Close();+ }+ }++ virtual Status Append(const Slice& data) {+ const char* src = data.data();+ size_t left = data.size();+ while (left > 0) {+ assert(base_ <= dst_);+ assert(dst_ <= limit_);+ size_t avail = limit_ - dst_;+ if (avail == 0) {+ if (!UnmapCurrentRegion() ||+ !MapNewRegion()) {+ return IOError(filename_, errno);+ }+ }++ size_t n = (left <= avail) ? left : avail;+ memcpy(dst_, src, n);+ dst_ += n;+ src += n;+ left -= n;+ }+ return Status::OK();+ }++ virtual Status Close() {+ Status s;+ size_t unused = limit_ - dst_;+ if (!UnmapCurrentRegion()) {+ s = IOError(filename_, errno);+ } else if (unused > 0) {+ // Trim the extra space at the end of the file+ if (ftruncate(fd_, file_offset_ - unused) < 0) {+ s = IOError(filename_, errno);+ }+ }++ if (close(fd_) < 0) {+ if (s.ok()) {+ s = IOError(filename_, errno);+ }+ }++ fd_ = -1;+ base_ = NULL;+ limit_ = NULL;+ return s;+ }++ virtual Status Flush() {+ return Status::OK();+ }++ virtual Status Sync() {+ Status s;++ if (pending_sync_) {+ // Some unmapped data was not synced+ pending_sync_ = false;+ if (fdatasync(fd_) < 0) {+ s = IOError(filename_, errno);+ }+ }++ if (dst_ > last_sync_) {+ // Find the beginnings of the pages that contain the first and last+ // bytes to be synced.+ size_t p1 = TruncateToPageBoundary(last_sync_ - base_);+ size_t p2 = TruncateToPageBoundary(dst_ - base_ - 1);+ last_sync_ = dst_;+ if (msync(base_ + p1, p2 - p1 + page_size_, MS_SYNC) < 0) {+ s = IOError(filename_, errno);+ }+ }++ return s;+ }+};++static int LockOrUnlock(int fd, bool lock) {+ errno = 0;+ struct flock f;+ memset(&f, 0, sizeof(f));+ f.l_type = (lock ? F_WRLCK : F_UNLCK);+ f.l_whence = SEEK_SET;+ f.l_start = 0;+ f.l_len = 0; // Lock/unlock entire file+ return fcntl(fd, F_SETLK, &f);+}++class PosixFileLock : public FileLock {+ public:+ int fd_;+};++class PosixEnv : public Env {+ public:+ PosixEnv();+ virtual ~PosixEnv() {+ fprintf(stderr, "Destroying Env::Default()\n");+ exit(1);+ }++ virtual Status NewSequentialFile(const std::string& fname,+ SequentialFile** result) {+ FILE* f = fopen(fname.c_str(), "r");+ if (f == NULL) {+ *result = NULL;+ return IOError(fname, errno);+ } else {+ *result = new PosixSequentialFile(fname, f);+ return Status::OK();+ }+ }++ virtual Status NewRandomAccessFile(const std::string& fname,+ RandomAccessFile** result) {+ int fd = open(fname.c_str(), O_RDONLY);+ if (fd < 0) {+ *result = NULL;+ return IOError(fname, errno);+ }+ *result = new PosixRandomAccessFile(fname, fd);+ return Status::OK();+ }++ virtual Status NewWritableFile(const std::string& fname,+ WritableFile** result) {+ Status s;+ const int fd = open(fname.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644);+ if (fd < 0) {+ *result = NULL;+ s = IOError(fname, errno);+ } else {+ *result = new PosixMmapFile(fname, fd, page_size_);+ }+ return s;+ }++ virtual bool FileExists(const std::string& fname) {+ return access(fname.c_str(), F_OK) == 0;+ }++ virtual Status GetChildren(const std::string& dir,+ std::vector<std::string>* result) {+ result->clear();+ DIR* d = opendir(dir.c_str());+ if (d == NULL) {+ return IOError(dir, errno);+ }+ struct dirent* entry;+ while ((entry = readdir(d)) != NULL) {+ result->push_back(entry->d_name);+ }+ closedir(d);+ return Status::OK();+ }++ virtual Status DeleteFile(const std::string& fname) {+ Status result;+ if (unlink(fname.c_str()) != 0) {+ result = IOError(fname, errno);+ }+ return result;+ };++ virtual Status CreateDir(const std::string& name) {+ Status result;+ if (mkdir(name.c_str(), 0755) != 0) {+ result = IOError(name, errno);+ }+ return result;+ };++ virtual Status DeleteDir(const std::string& name) {+ Status result;+ if (rmdir(name.c_str()) != 0) {+ result = IOError(name, errno);+ }+ return result;+ };++ virtual Status GetFileSize(const std::string& fname, uint64_t* size) {+ Status s;+ struct stat sbuf;+ if (stat(fname.c_str(), &sbuf) != 0) {+ *size = 0;+ s = IOError(fname, errno);+ } else {+ *size = sbuf.st_size;+ }+ return s;+ }++ virtual Status RenameFile(const std::string& src, const std::string& target) {+ Status result;+ if (rename(src.c_str(), target.c_str()) != 0) {+ result = IOError(src, errno);+ }+ return result;+ }++ virtual Status LockFile(const std::string& fname, FileLock** lock) {+ *lock = NULL;+ Status result;+ int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644);+ if (fd < 0) {+ result = IOError(fname, errno);+ } else if (LockOrUnlock(fd, true) == -1) {+ result = IOError("lock " + fname, errno);+ close(fd);+ } else {+ PosixFileLock* my_lock = new PosixFileLock;+ my_lock->fd_ = fd;+ *lock = my_lock;+ }+ return result;+ }++ virtual Status UnlockFile(FileLock* lock) {+ PosixFileLock* my_lock = reinterpret_cast<PosixFileLock*>(lock);+ Status result;+ if (LockOrUnlock(my_lock->fd_, false) == -1) {+ result = IOError("unlock", errno);+ }+ close(my_lock->fd_);+ delete my_lock;+ return result;+ }++ virtual void Schedule(void (*function)(void*), void* arg);++ virtual void StartThread(void (*function)(void* arg), void* arg);++ virtual Status GetTestDirectory(std::string* result) {+ const char* env = getenv("TEST_TMPDIR");+ if (env && env[0] != '\0') {+ *result = env;+ } else {+ char buf[100];+ snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid()));+ *result = buf;+ }+ // Directory may already exist+ CreateDir(*result);+ return Status::OK();+ }++ static uint64_t gettid() {+ pthread_t tid = pthread_self();+ uint64_t thread_id = 0;+ memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid)));+ return thread_id;+ }++ virtual Status NewLogger(const std::string& fname, Logger** result) {+ FILE* f = fopen(fname.c_str(), "w");+ if (f == NULL) {+ *result = NULL;+ return IOError(fname, errno);+ } else {+ *result = new PosixLogger(f, &PosixEnv::gettid);+ return Status::OK();+ }+ }++ virtual uint64_t NowMicros() {+ struct timeval tv;+ gettimeofday(&tv, NULL);+ return static_cast<uint64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;+ }++ virtual void SleepForMicroseconds(int micros) {+ usleep(micros);+ }++ private:+ void PthreadCall(const char* label, int result) {+ if (result != 0) {+ fprintf(stderr, "pthread %s: %s\n", label, strerror(result));+ exit(1);+ }+ }++ // BGThread() is the body of the background thread+ void BGThread();+ static void* BGThreadWrapper(void* arg) {+ reinterpret_cast<PosixEnv*>(arg)->BGThread();+ return NULL;+ }++ size_t page_size_;+ pthread_mutex_t mu_;+ pthread_cond_t bgsignal_;+ pthread_t bgthread_;+ bool started_bgthread_;++ // Entry per Schedule() call+ struct BGItem { void* arg; void (*function)(void*); };+ typedef std::deque<BGItem> BGQueue;+ BGQueue queue_;+};++PosixEnv::PosixEnv() : page_size_(getpagesize()),+ started_bgthread_(false) {+ PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL));+ PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL));+}++void PosixEnv::Schedule(void (*function)(void*), void* arg) {+ PthreadCall("lock", pthread_mutex_lock(&mu_));++ // Start background thread if necessary+ if (!started_bgthread_) {+ started_bgthread_ = true;+ PthreadCall(+ "create thread",+ pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this));+ }++ // If the queue is currently empty, the background thread may currently be+ // waiting.+ if (queue_.empty()) {+ PthreadCall("signal", pthread_cond_signal(&bgsignal_));+ }++ // Add to priority queue+ queue_.push_back(BGItem());+ queue_.back().function = function;+ queue_.back().arg = arg;++ PthreadCall("unlock", pthread_mutex_unlock(&mu_));+}++void PosixEnv::BGThread() {+ while (true) {+ // Wait until there is an item that is ready to run+ PthreadCall("lock", pthread_mutex_lock(&mu_));+ while (queue_.empty()) {+ PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_));+ }++ void (*function)(void*) = queue_.front().function;+ void* arg = queue_.front().arg;+ queue_.pop_front();++ PthreadCall("unlock", pthread_mutex_unlock(&mu_));+ (*function)(arg);+ }+}++namespace {+struct StartThreadState {+ void (*user_function)(void*);+ void* arg;+};+}+static void* StartThreadWrapper(void* arg) {+ StartThreadState* state = reinterpret_cast<StartThreadState*>(arg);+ state->user_function(state->arg);+ delete state;+ return NULL;+}++void PosixEnv::StartThread(void (*function)(void* arg), void* arg) {+ pthread_t t;+ StartThreadState* state = new StartThreadState;+ state->user_function = function;+ state->arg = arg;+ PthreadCall("start thread",+ pthread_create(&t, NULL, &StartThreadWrapper, state));+}++} // namespace++static pthread_once_t once = PTHREAD_ONCE_INIT;+static Env* default_env;+static void InitDefaultEnv() { default_env = new PosixEnv; }++Env* Env::Default() {+ pthread_once(&once, InitDefaultEnv);+ return default_env;+}++} // namespace leveldb
+ cbits/leveldb/util/env_test.cc view
@@ -0,0 +1,104 @@+// 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/env.h"++#include "port/port.h"+#include "util/testharness.h"++namespace leveldb {++static const int kDelayMicros = 100000;++class EnvPosixTest {+ private:+ port::Mutex mu_;+ std::string events_;++ public:+ Env* env_;+ EnvPosixTest() : env_(Env::Default()) { }+};++static void SetBool(void* ptr) {+ reinterpret_cast<port::AtomicPointer*>(ptr)->NoBarrier_Store(ptr);+}++TEST(EnvPosixTest, RunImmediately) {+ port::AtomicPointer called (NULL);+ env_->Schedule(&SetBool, &called);+ Env::Default()->SleepForMicroseconds(kDelayMicros);+ ASSERT_TRUE(called.NoBarrier_Load() != NULL);+}++TEST(EnvPosixTest, RunMany) {+ port::AtomicPointer last_id (NULL);++ struct CB {+ port::AtomicPointer* last_id_ptr; // Pointer to shared slot+ uintptr_t id; // Order# for the execution of this callback++ CB(port::AtomicPointer* p, int i) : last_id_ptr(p), id(i) { }++ static void Run(void* v) {+ CB* cb = reinterpret_cast<CB*>(v);+ void* cur = cb->last_id_ptr->NoBarrier_Load();+ ASSERT_EQ(cb->id-1, reinterpret_cast<uintptr_t>(cur));+ cb->last_id_ptr->Release_Store(reinterpret_cast<void*>(cb->id));+ }+ };++ // Schedule in different order than start time+ CB cb1(&last_id, 1);+ CB cb2(&last_id, 2);+ CB cb3(&last_id, 3);+ CB cb4(&last_id, 4);+ env_->Schedule(&CB::Run, &cb1);+ env_->Schedule(&CB::Run, &cb2);+ env_->Schedule(&CB::Run, &cb3);+ env_->Schedule(&CB::Run, &cb4);++ Env::Default()->SleepForMicroseconds(kDelayMicros);+ void* cur = last_id.Acquire_Load();+ ASSERT_EQ(4, reinterpret_cast<uintptr_t>(cur));+}++struct State {+ port::Mutex mu;+ int val;+ int num_running;+};++static void ThreadBody(void* arg) {+ State* s = reinterpret_cast<State*>(arg);+ s->mu.Lock();+ s->val += 1;+ s->num_running -= 1;+ s->mu.Unlock();+}++TEST(EnvPosixTest, StartThread) {+ State state;+ state.val = 0;+ state.num_running = 3;+ for (int i = 0; i < 3; i++) {+ env_->StartThread(&ThreadBody, &state);+ }+ while (true) {+ state.mu.Lock();+ int num = state.num_running;+ state.mu.Unlock();+ if (num == 0) {+ break;+ }+ Env::Default()->SleepForMicroseconds(kDelayMicros);+ }+ ASSERT_EQ(state.val, 3);+}++} // namespace leveldb++int main(int argc, char** argv) {+ return leveldb::test::RunAllTests();+}
+ cbits/leveldb/util/hash.cc view
@@ -0,0 +1,45 @@+// 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 <string.h>+#include "util/coding.h"+#include "util/hash.h"++namespace leveldb {++uint32_t Hash(const char* data, size_t n, uint32_t seed) {+ // Similar to murmur hash+ const uint32_t m = 0xc6a4a793;+ const uint32_t r = 24;+ const char* limit = data + n;+ uint32_t h = seed ^ (n * m);++ // Pick up four bytes at a time+ while (data + 4 <= limit) {+ uint32_t w = DecodeFixed32(data);+ data += 4;+ h += w;+ h *= m;+ h ^= (h >> 16);+ }++ // Pick up remaining bytes+ switch (limit - data) {+ case 3:+ h += data[2] << 16;+ // fall through+ case 2:+ h += data[1] << 8;+ // fall through+ case 1:+ h += data[0];+ h *= m;+ h ^= (h >> r);+ break;+ }+ return h;+}+++} // namespace leveldb
+ cbits/leveldb/util/hash.h view
@@ -0,0 +1,19 @@+// 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.+//+// Simple hash function used for internal data structures++#ifndef STORAGE_LEVELDB_UTIL_HASH_H_+#define STORAGE_LEVELDB_UTIL_HASH_H_++#include <stddef.h>+#include <stdint.h>++namespace leveldb {++extern uint32_t Hash(const char* data, size_t n, uint32_t seed);++}++#endif // STORAGE_LEVELDB_UTIL_HASH_H_
+ cbits/leveldb/util/histogram.cc view
@@ -0,0 +1,139 @@+// 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 <math.h>+#include <stdio.h>+#include "port/port.h"+#include "util/histogram.h"++namespace leveldb {++const double Histogram::kBucketLimit[kNumBuckets] = {+ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45,+ 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450,+ 500, 600, 700, 800, 900, 1000, 1200, 1400, 1600, 1800, 2000, 2500, 3000,+ 3500, 4000, 4500, 5000, 6000, 7000, 8000, 9000, 10000, 12000, 14000,+ 16000, 18000, 20000, 25000, 30000, 35000, 40000, 45000, 50000, 60000,+ 70000, 80000, 90000, 100000, 120000, 140000, 160000, 180000, 200000,+ 250000, 300000, 350000, 400000, 450000, 500000, 600000, 700000, 800000,+ 900000, 1000000, 1200000, 1400000, 1600000, 1800000, 2000000, 2500000,+ 3000000, 3500000, 4000000, 4500000, 5000000, 6000000, 7000000, 8000000,+ 9000000, 10000000, 12000000, 14000000, 16000000, 18000000, 20000000,+ 25000000, 30000000, 35000000, 40000000, 45000000, 50000000, 60000000,+ 70000000, 80000000, 90000000, 100000000, 120000000, 140000000, 160000000,+ 180000000, 200000000, 250000000, 300000000, 350000000, 400000000,+ 450000000, 500000000, 600000000, 700000000, 800000000, 900000000,+ 1000000000, 1200000000, 1400000000, 1600000000, 1800000000, 2000000000,+ 2500000000.0, 3000000000.0, 3500000000.0, 4000000000.0, 4500000000.0,+ 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0,+ 1e200,+};++void Histogram::Clear() {+ min_ = kBucketLimit[kNumBuckets-1];+ max_ = 0;+ num_ = 0;+ sum_ = 0;+ sum_squares_ = 0;+ for (int i = 0; i < kNumBuckets; i++) {+ buckets_[i] = 0;+ }+}++void Histogram::Add(double value) {+ // Linear search is fast enough for our usage in db_bench+ int b = 0;+ while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) {+ b++;+ }+ buckets_[b] += 1.0;+ if (min_ > value) min_ = value;+ if (max_ < value) max_ = value;+ num_++;+ sum_ += value;+ sum_squares_ += (value * value);+}++void Histogram::Merge(const Histogram& other) {+ if (other.min_ < min_) min_ = other.min_;+ if (other.max_ > max_) max_ = other.max_;+ num_ += other.num_;+ sum_ += other.sum_;+ sum_squares_ += other.sum_squares_;+ for (int b = 0; b < kNumBuckets; b++) {+ buckets_[b] += other.buckets_[b];+ }+}++double Histogram::Median() const {+ return Percentile(50.0);+}++double Histogram::Percentile(double p) const {+ double threshold = num_ * (p / 100.0);+ double sum = 0;+ for (int b = 0; b < kNumBuckets; b++) {+ sum += buckets_[b];+ if (sum >= threshold) {+ // Scale linearly within this bucket+ double left_point = (b == 0) ? 0 : kBucketLimit[b-1];+ double right_point = kBucketLimit[b];+ double left_sum = sum - buckets_[b];+ double right_sum = sum;+ double pos = (threshold - left_sum) / (right_sum - left_sum);+ double r = left_point + (right_point - left_point) * pos;+ if (r < min_) r = min_;+ if (r > max_) r = max_;+ return r;+ }+ }+ return max_;+}++double Histogram::Average() const {+ if (num_ == 0.0) return 0;+ return sum_ / num_;+}++double Histogram::StandardDeviation() const {+ if (num_ == 0.0) return 0;+ double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_);+ return sqrt(variance);+}++std::string Histogram::ToString() const {+ std::string r;+ char buf[200];+ snprintf(buf, sizeof(buf),+ "Count: %.0f Average: %.4f StdDev: %.2f\n",+ num_, Average(), StandardDeviation());+ r.append(buf);+ snprintf(buf, sizeof(buf),+ "Min: %.4f Median: %.4f Max: %.4f\n",+ (num_ == 0.0 ? 0.0 : min_), Median(), max_);+ r.append(buf);+ r.append("------------------------------------------------------\n");+ const double mult = 100.0 / num_;+ double sum = 0;+ for (int b = 0; b < kNumBuckets; b++) {+ if (buckets_[b] <= 0.0) continue;+ sum += buckets_[b];+ snprintf(buf, sizeof(buf),+ "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ",+ ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left+ kBucketLimit[b], // right+ buckets_[b], // count+ mult * buckets_[b], // percentage+ mult * sum); // cumulative percentage+ r.append(buf);++ // Add hash marks based on percentage; 20 marks for 100%.+ int marks = static_cast<int>(20*(buckets_[b] / num_) + 0.5);+ r.append(marks, '#');+ r.push_back('\n');+ }+ return r;+}++} // namespace leveldb
+ cbits/leveldb/util/histogram.h view
@@ -0,0 +1,42 @@+// 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.++#ifndef STORAGE_LEVELDB_UTIL_HISTOGRAM_H_+#define STORAGE_LEVELDB_UTIL_HISTOGRAM_H_++#include <string>++namespace leveldb {++class Histogram {+ public:+ Histogram() { }+ ~Histogram() { }++ void Clear();+ void Add(double value);+ void Merge(const Histogram& other);++ std::string ToString() const;++ private:+ double min_;+ double max_;+ double num_;+ double sum_;+ double sum_squares_;++ enum { kNumBuckets = 154 };+ static const double kBucketLimit[kNumBuckets];+ double buckets_[kNumBuckets];++ double Median() const;+ double Percentile(double p) const;+ double Average() const;+ double StandardDeviation() const;+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_
+ cbits/leveldb/util/logging.cc view
@@ -0,0 +1,81 @@+// 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 "util/logging.h"++#include <errno.h>+#include <stdarg.h>+#include <stdio.h>+#include <stdlib.h>+#include "leveldb/env.h"+#include "leveldb/slice.h"++namespace leveldb {++void AppendNumberTo(std::string* str, uint64_t num) {+ char buf[30];+ snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num);+ str->append(buf);+}++void AppendEscapedStringTo(std::string* str, const Slice& value) {+ for (size_t i = 0; i < value.size(); i++) {+ char c = value[i];+ if (c >= ' ' && c <= '~') {+ str->push_back(c);+ } else {+ char buf[10];+ snprintf(buf, sizeof(buf), "\\x%02x",+ static_cast<unsigned int>(c) & 0xff);+ str->append(buf);+ }+ }+}++std::string NumberToString(uint64_t num) {+ std::string r;+ AppendNumberTo(&r, num);+ return r;+}++std::string EscapeString(const Slice& value) {+ std::string r;+ AppendEscapedStringTo(&r, value);+ return r;+}++bool ConsumeChar(Slice* in, char c) {+ if (!in->empty() && (*in)[0] == c) {+ in->remove_prefix(1);+ return true;+ } else {+ return false;+ }+}++bool ConsumeDecimalNumber(Slice* in, uint64_t* val) {+ uint64_t v = 0;+ int digits = 0;+ while (!in->empty()) {+ char c = (*in)[0];+ if (c >= '0' && c <= '9') {+ ++digits;+ const int delta = (c - '0');+ static const uint64_t kMaxUint64 = ~static_cast<uint64_t>(0);+ if (v > kMaxUint64/10 ||+ (v == kMaxUint64/10 && delta > kMaxUint64%10)) {+ // Overflow+ return false;+ }+ v = (v * 10) + delta;+ in->remove_prefix(1);+ } else {+ break;+ }+ }+ *val = v;+ return (digits > 0);+}++} // namespace leveldb
+ cbits/leveldb/util/logging.h view
@@ -0,0 +1,47 @@+// 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.+//+// Must not be included from any .h files to avoid polluting the namespace+// with macros.++#ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_+#define STORAGE_LEVELDB_UTIL_LOGGING_H_++#include <stdio.h>+#include <stdint.h>+#include <string>+#include "port/port.h"++namespace leveldb {++class Slice;+class WritableFile;++// Append a human-readable printout of "num" to *str+extern void AppendNumberTo(std::string* str, uint64_t num);++// Append a human-readable printout of "value" to *str.+// Escapes any non-printable characters found in "value".+extern void AppendEscapedStringTo(std::string* str, const Slice& value);++// Return a human-readable printout of "num"+extern std::string NumberToString(uint64_t num);++// Return a human-readable version of "value".+// Escapes any non-printable characters found in "value".+extern std::string EscapeString(const Slice& value);++// If *in starts with "c", advances *in past the first character and+// returns true. Otherwise, returns false.+extern bool ConsumeChar(Slice* in, char c);++// Parse a human-readable number from "*in" into *value. On success,+// advances "*in" past the consumed number and sets "*val" to the+// numeric value. Otherwise, returns false and leaves *in in an+// unspecified state.+extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val);++} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_
+ cbits/leveldb/util/mutexlock.h view
@@ -0,0 +1,39 @@+// 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.++#ifndef STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_+#define STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_++#include "port/port.h"++namespace leveldb {++// Helper class that locks a mutex on construction and unlocks the mutex when+// the destructor of the MutexLock object is invoked.+//+// Typical usage:+//+// void MyClass::MyMethod() {+// MutexLock l(&mu_); // mu_ is an instance variable+// ... some complex code, possibly with multiple return paths ...+// }++class MutexLock {+ public:+ explicit MutexLock(port::Mutex *mu) : mu_(mu) {+ this->mu_->Lock();+ }+ ~MutexLock() { this->mu_->Unlock(); }++ private:+ port::Mutex *const mu_;+ // No copying allowed+ MutexLock(const MutexLock&);+ void operator=(const MutexLock&);+};++} // namespace leveldb+++#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_
+ cbits/leveldb/util/options.cc view
@@ -0,0 +1,28 @@+// 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/options.h"++#include "leveldb/comparator.h"+#include "leveldb/env.h"++namespace leveldb {++Options::Options()+ : comparator(BytewiseComparator()),+ create_if_missing(false),+ error_if_exists(false),+ paranoid_checks(false),+ env(Env::Default()),+ info_log(NULL),+ write_buffer_size(4<<20),+ max_open_files(1000),+ block_cache(NULL),+ block_size(4096),+ block_restart_interval(16),+ compression(kSnappyCompression) {+}+++} // namespace leveldb
+ cbits/leveldb/util/posix_logger.h view
@@ -0,0 +1,98 @@+// 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.+//+// Logger implementation that can be shared by all environments+// where enough posix functionality is available.++#ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_+#define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_++#include <algorithm>+#include <stdio.h>+#include <sys/time.h>+#include <time.h>+#include "leveldb/env.h"++namespace leveldb {++class PosixLogger : public Logger {+ private:+ FILE* file_;+ uint64_t (*gettid_)(); // Return the thread id for the current thread+ public:+ PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { }+ virtual ~PosixLogger() {+ fclose(file_);+ }+ virtual void Logv(const char* format, va_list ap) {+ const uint64_t thread_id = (*gettid_)();++ // We try twice: the first time with a fixed-size stack allocated buffer,+ // and the second time with a much larger dynamically allocated buffer.+ char buffer[500];+ for (int iter = 0; iter < 2; iter++) {+ char* base;+ int bufsize;+ if (iter == 0) {+ bufsize = sizeof(buffer);+ base = buffer;+ } else {+ bufsize = 30000;+ base = new char[bufsize];+ }+ char* p = base;+ char* limit = base + bufsize;++ struct timeval now_tv;+ gettimeofday(&now_tv, NULL);+ const time_t seconds = now_tv.tv_sec;+ struct tm t;+ localtime_r(&seconds, &t);+ p += snprintf(p, limit - p,+ "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ",+ t.tm_year + 1900,+ t.tm_mon + 1,+ t.tm_mday,+ t.tm_hour,+ t.tm_min,+ t.tm_sec,+ static_cast<int>(now_tv.tv_usec),+ static_cast<long long unsigned int>(thread_id));++ // Print the message+ if (p < limit) {+ va_list backup_ap;+ va_copy(backup_ap, ap);+ p += vsnprintf(p, limit - p, format, backup_ap);+ va_end(backup_ap);+ }++ // Truncate to available space if necessary+ if (p >= limit) {+ if (iter == 0) {+ continue; // Try again with larger buffer+ } else {+ p = limit - 1;+ }+ }++ // Add newline if necessary+ if (p == base || p[-1] != '\n') {+ *p++ = '\n';+ }++ assert(p <= limit);+ fwrite(base, 1, p - base, file_);+ fflush(file_);+ if (base != buffer) {+ delete[] base;+ }+ break;+ }+ }+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
+ cbits/leveldb/util/random.h view
@@ -0,0 +1,59 @@+// 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.++#ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_+#define STORAGE_LEVELDB_UTIL_RANDOM_H_++#include <stdint.h>++namespace leveldb {++// A very simple random number generator. Not especially good at+// generating truly random bits, but good enough for our needs in this+// package.+class Random {+ private:+ uint32_t seed_;+ public:+ explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { }+ uint32_t Next() {+ static const uint32_t M = 2147483647L; // 2^31-1+ static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0+ // We are computing+ // seed_ = (seed_ * A) % M, where M = 2^31-1+ //+ // seed_ must not be zero or M, or else all subsequent computed values+ // will be zero or M respectively. For all other values, seed_ will end+ // up cycling through every number in [1,M-1]+ uint64_t product = seed_ * A;++ // Compute (product % M) using the fact that ((x << 31) % M) == x.+ seed_ = static_cast<uint32_t>((product >> 31) + (product & M));+ // The first reduction may overflow by 1 bit, so we may need to+ // repeat. mod == M is not possible; using > allows the faster+ // sign-bit-based test.+ if (seed_ > M) {+ seed_ -= M;+ }+ return seed_;+ }+ // Returns a uniformly distributed value in the range [0..n-1]+ // REQUIRES: n > 0+ uint32_t Uniform(int n) { return Next() % n; }++ // Randomly returns true ~"1/n" of the time, and false otherwise.+ // REQUIRES: n > 0+ bool OneIn(int n) { return (Next() % n) == 0; }++ // Skewed: pick "base" uniformly from range [0,max_log] and then+ // return "base" random bits. The effect is to pick a number in the+ // range [0,2^max_log-1] with exponential bias towards smaller numbers.+ uint32_t Skewed(int max_log) {+ return Uniform(1 << Uniform(max_log + 1));+ }+};++} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_RANDOM_H_
+ cbits/leveldb/util/status.cc view
@@ -0,0 +1,75 @@+// 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 <stdio.h>+#include "port/port.h"+#include "leveldb/status.h"++namespace leveldb {++const char* Status::CopyState(const char* state) {+ uint32_t size;+ memcpy(&size, state, sizeof(size));+ char* result = new char[size + 5];+ memcpy(result, state, size + 5);+ return result;+}++Status::Status(Code code, const Slice& msg, const Slice& msg2) {+ assert(code != kOk);+ const uint32_t len1 = msg.size();+ const uint32_t len2 = msg2.size();+ const uint32_t size = len1 + (len2 ? (2 + len2) : 0);+ char* result = new char[size + 5];+ memcpy(result, &size, sizeof(size));+ result[4] = static_cast<char>(code);+ memcpy(result + 5, msg.data(), len1);+ if (len2) {+ result[5 + len1] = ':';+ result[6 + len1] = ' ';+ memcpy(result + 7 + len1, msg2.data(), len2);+ }+ state_ = result;+}++std::string Status::ToString() const {+ if (state_ == NULL) {+ return "OK";+ } else {+ char tmp[30];+ const char* type;+ switch (code()) {+ case kOk:+ type = "OK";+ break;+ case kNotFound:+ type = "NotFound: ";+ break;+ case kCorruption:+ type = "Corruption: ";+ break;+ case kNotSupported:+ type = "Not implemented: ";+ break;+ case kInvalidArgument:+ type = "Invalid argument: ";+ break;+ case kIOError:+ type = "IO error: ";+ break;+ default:+ snprintf(tmp, sizeof(tmp), "Unknown code(%d): ",+ static_cast<int>(code()));+ type = tmp;+ break;+ }+ std::string result(type);+ uint32_t length;+ memcpy(&length, state_, sizeof(length));+ result.append(state_ + 5, length);+ return result;+ }+}++} // namespace leveldb
+ cbits/leveldb/util/testharness.cc view
@@ -0,0 +1,77 @@+// 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 "util/testharness.h"++#include <string>+#include <stdlib.h>+#include <sys/stat.h>+#include <sys/types.h>++namespace leveldb {+namespace test {++namespace {+struct Test {+ const char* base;+ const char* name;+ void (*func)();+};+std::vector<Test>* tests;+}++bool RegisterTest(const char* base, const char* name, void (*func)()) {+ if (tests == NULL) {+ tests = new std::vector<Test>;+ }+ Test t;+ t.base = base;+ t.name = name;+ t.func = func;+ tests->push_back(t);+ return true;+}++int RunAllTests() {+ const char* matcher = getenv("LEVELDB_TESTS");++ int num = 0;+ if (tests != NULL) {+ for (int i = 0; i < tests->size(); i++) {+ const Test& t = (*tests)[i];+ if (matcher != NULL) {+ std::string name = t.base;+ name.push_back('.');+ name.append(t.name);+ if (strstr(name.c_str(), matcher) == NULL) {+ continue;+ }+ }+ fprintf(stderr, "==== Test %s.%s\n", t.base, t.name);+ (*t.func)();+ ++num;+ }+ }+ fprintf(stderr, "==== PASSED %d tests\n", num);+ return 0;+}++std::string TmpDir() {+ std::string dir;+ Status s = Env::Default()->GetTestDirectory(&dir);+ ASSERT_TRUE(s.ok()) << s.ToString();+ return dir;+}++int RandomSeed() {+ const char* env = getenv("TEST_RANDOM_SEED");+ int result = (env != NULL ? atoi(env) : 301);+ if (result <= 0) {+ result = 301;+ }+ return result;+}++} // namespace test+} // namespace leveldb
+ cbits/leveldb/util/testharness.h view
@@ -0,0 +1,138 @@+// 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.++#ifndef STORAGE_LEVELDB_UTIL_TESTHARNESS_H_+#define STORAGE_LEVELDB_UTIL_TESTHARNESS_H_++#include <stdio.h>+#include <stdlib.h>+#include <sstream>+#include "leveldb/env.h"+#include "leveldb/slice.h"+#include "util/random.h"++namespace leveldb {+namespace test {++// Run some of the tests registered by the TEST() macro. If the+// environment variable "LEVELDB_TESTS" is not set, runs all tests.+// Otherwise, runs only the tests whose name contains the value of+// "LEVELDB_TESTS" as a substring. E.g., suppose the tests are:+// TEST(Foo, Hello) { ... }+// TEST(Foo, World) { ... }+// LEVELDB_TESTS=Hello will run the first test+// LEVELDB_TESTS=o will run both tests+// LEVELDB_TESTS=Junk will run no tests+//+// Returns 0 if all tests pass.+// Dies or returns a non-zero value if some test fails.+extern int RunAllTests();++// Return the directory to use for temporary storage.+extern std::string TmpDir();++// Return a randomization seed for this run. Typically returns the+// same number on repeated invocations of this binary, but automated+// runs may be able to vary the seed.+extern int RandomSeed();++// An instance of Tester is allocated to hold temporary state during+// the execution of an assertion.+class Tester {+ private:+ bool ok_;+ const char* fname_;+ int line_;+ std::stringstream ss_;++ public:+ Tester(const char* f, int l)+ : ok_(true), fname_(f), line_(l) {+ }++ ~Tester() {+ if (!ok_) {+ fprintf(stderr, "%s:%d:%s\n", fname_, line_, ss_.str().c_str());+ exit(1);+ }+ }++ Tester& Is(bool b, const char* msg) {+ if (!b) {+ ss_ << " Assertion failure " << msg;+ ok_ = false;+ }+ return *this;+ }++ Tester& IsOk(const Status& s) {+ if (!s.ok()) {+ ss_ << " " << s.ToString();+ ok_ = false;+ }+ return *this;+ }++#define BINARY_OP(name,op) \+ template <class X, class Y> \+ Tester& name(const X& x, const Y& y) { \+ if (! (x op y)) { \+ ss_ << " failed: " << x << (" " #op " ") << y; \+ ok_ = false; \+ } \+ return *this; \+ }++ BINARY_OP(IsEq, ==)+ BINARY_OP(IsNe, !=)+ BINARY_OP(IsGe, >=)+ BINARY_OP(IsGt, >)+ BINARY_OP(IsLe, <=)+ BINARY_OP(IsLt, <)+#undef BINARY_OP++ // Attach the specified value to the error message if an error has occurred+ template <class V>+ Tester& operator<<(const V& value) {+ if (!ok_) {+ ss_ << " " << value;+ }+ return *this;+ }+};++#define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c)+#define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s))+#define ASSERT_EQ(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a),(b))+#define ASSERT_NE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a),(b))+#define ASSERT_GE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a),(b))+#define ASSERT_GT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a),(b))+#define ASSERT_LE(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a),(b))+#define ASSERT_LT(a,b) ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a),(b))++#define TCONCAT(a,b) TCONCAT1(a,b)+#define TCONCAT1(a,b) a##b++#define TEST(base,name) \+class TCONCAT(_Test_,name) : public base { \+ public: \+ void _Run(); \+ static void _RunIt() { \+ TCONCAT(_Test_,name) t; \+ t._Run(); \+ } \+}; \+bool TCONCAT(_Test_ignored_,name) = \+ ::leveldb::test::RegisterTest(#base, #name, &TCONCAT(_Test_,name)::_RunIt); \+void TCONCAT(_Test_,name)::_Run()++// Register the specified test. Typically not used directly, but+// invoked via the macro expansion of TEST.+extern bool RegisterTest(const char* base, const char* name, void (*func)());+++} // namespace test+} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_TESTHARNESS_H_
+ cbits/leveldb/util/testutil.cc view
@@ -0,0 +1,51 @@+// 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 "util/testutil.h"++#include "util/random.h"++namespace leveldb {+namespace test {++Slice RandomString(Random* rnd, int len, std::string* dst) {+ dst->resize(len);+ for (int i = 0; i < len; i++) {+ (*dst)[i] = static_cast<char>(' ' + rnd->Uniform(95)); // ' ' .. '~'+ }+ return Slice(*dst);+}++std::string RandomKey(Random* rnd, int len) {+ // Make sure to generate a wide variety of characters so we+ // test the boundary conditions for short-key optimizations.+ static const char kTestChars[] = {+ '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff'+ };+ std::string result;+ for (int i = 0; i < len; i++) {+ result += kTestChars[rnd->Uniform(sizeof(kTestChars))];+ }+ return result;+}+++extern Slice CompressibleString(Random* rnd, double compressed_fraction,+ int len, std::string* dst) {+ int raw = static_cast<int>(len * compressed_fraction);+ if (raw < 1) raw = 1;+ std::string raw_data;+ RandomString(rnd, raw, &raw_data);++ // Duplicate the random data until we have filled "len" bytes+ dst->clear();+ while (dst->size() < len) {+ dst->append(raw_data);+ }+ dst->resize(len);+ return Slice(*dst);+}++} // namespace test+} // namespace leveldb
+ cbits/leveldb/util/testutil.h view
@@ -0,0 +1,53 @@+// 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.++#ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_+#define STORAGE_LEVELDB_UTIL_TESTUTIL_H_++#include "leveldb/env.h"+#include "leveldb/slice.h"+#include "util/random.h"++namespace leveldb {+namespace test {++// Store in *dst a random string of length "len" and return a Slice that+// references the generated data.+extern Slice RandomString(Random* rnd, int len, std::string* dst);++// Return a random key with the specified length that may contain interesting+// characters (e.g. \x00, \xff, etc.).+extern std::string RandomKey(Random* rnd, int len);++// Store in *dst a string of length "len" that will compress to+// "N*compressed_fraction" bytes and return a Slice that references+// the generated data.+extern Slice CompressibleString(Random* rnd, double compressed_fraction,+ int len, std::string* dst);++// A wrapper that allows injection of errors.+class ErrorEnv : public EnvWrapper {+ public:+ bool writable_file_error_;+ int num_writable_file_errors_;++ ErrorEnv() : EnvWrapper(Env::Default()),+ writable_file_error_(false),+ num_writable_file_errors_(0) { }++ virtual Status NewWritableFile(const std::string& fname,+ WritableFile** result) {+ if (writable_file_error_) {+ ++num_writable_file_errors_;+ *result = NULL;+ return Status::IOError(fname, "fake error");+ }+ return target()->NewWritableFile(fname, result);+ }+};++} // namespace test+} // namespace leveldb++#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_
configure view
@@ -1,4 +1,4165 @@-#!/bin/sh--make --directory=cbits/leveldb libleveldb.a-echo extra-lib-dirs: `pwd`/cbits/leveldb > leveldb-haskell.buildinfo+#! /bin/sh+# Guess values for system-dependent variables and create Makefiles.+# Generated by GNU Autoconf 2.61 for Haskell LevelDB package 0.1.+#+# Report bugs to <mad.one@gmail.com>.+#+# Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,+# 2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.+# This configure script is free software; the Free Software Foundation+# gives unlimited permission to copy, distribute and modify it.+## --------------------- ##+## M4sh Initialization. ##+## --------------------- ##++# Be more Bourne compatible+DUALCASE=1; export DUALCASE # for MKS sh+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then+ emulate sh+ NULLCMD=:+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which+ # is contrary to our usage. Disable this feature.+ alias -g '${1+"$@"}'='"$@"'+ setopt NO_GLOB_SUBST+else+ case `(set -o) 2>/dev/null` in+ *posix*) set -o posix ;;+esac++fi+++++# PATH needs CR+# Avoid depending upon Character Ranges.+as_cr_letters='abcdefghijklmnopqrstuvwxyz'+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'+as_cr_Letters=$as_cr_letters$as_cr_LETTERS+as_cr_digits='0123456789'+as_cr_alnum=$as_cr_Letters$as_cr_digits++# The user is always right.+if test "${PATH_SEPARATOR+set}" != set; then+ echo "#! /bin/sh" >conf$$.sh+ echo "exit 0" >>conf$$.sh+ chmod +x conf$$.sh+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then+ PATH_SEPARATOR=';'+ else+ PATH_SEPARATOR=:+ fi+ rm -f conf$$.sh+fi++# Support unset when possible.+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then+ as_unset=unset+else+ as_unset=false+fi+++# IFS+# We need space, tab and new line, in precisely that order. Quoting is+# there to prevent editors from complaining about space-tab.+# (If _AS_PATH_WALK were called with IFS unset, it would disable word+# splitting by setting IFS to empty value.)+as_nl='+'+IFS=" "" $as_nl"++# Find who we are. Look in the path if we contain no directory separator.+case $0 in+ *[\\/]* ) as_myself=$0 ;;+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break+done+IFS=$as_save_IFS++ ;;+esac+# We did not find ourselves, most probably we were run as `sh COMMAND'+# in which case we are not to be found in the path.+if test "x$as_myself" = x; then+ as_myself=$0+fi+if test ! -f "$as_myself"; then+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2+ { (exit 1); exit 1; }+fi++# Work around bugs in pre-3.0 UWIN ksh.+for as_var in ENV MAIL MAILPATH+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+done+PS1='$ '+PS2='> '+PS4='+ '++# NLS nuisances.+for as_var in \+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \+ LC_TELEPHONE LC_TIME+do+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then+ eval $as_var=C; export $as_var+ else+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+ fi+done++# Required to use basename.+if expr a : '\(a\)' >/dev/null 2>&1 &&+ test "X`expr 00001 : '.*\(...\)'`" = X001; then+ as_expr=expr+else+ as_expr=false+fi++if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then+ as_basename=basename+else+ as_basename=false+fi+++# Name of the executable.+as_me=`$as_basename -- "$0" ||+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \+ X"$0" : 'X\(//\)$' \| \+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||+echo X/"$0" |+ sed '/^.*\/\([^/][^/]*\)\/*$/{+ s//\1/+ q+ }+ /^X\/\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\/\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`++# CDPATH.+$as_unset CDPATH+++if test "x$CONFIG_SHELL" = x; then+ if (eval ":") 2>/dev/null; then+ as_have_required=yes+else+ as_have_required=no+fi++ if test $as_have_required = yes && (eval ":+(as_func_return () {+ (exit \$1)+}+as_func_success () {+ as_func_return 0+}+as_func_failure () {+ as_func_return 1+}+as_func_ret_success () {+ return 0+}+as_func_ret_failure () {+ return 1+}++exitcode=0+if as_func_success; then+ :+else+ exitcode=1+ echo as_func_success failed.+fi++if as_func_failure; then+ exitcode=1+ echo as_func_failure succeeded.+fi++if as_func_ret_success; then+ :+else+ exitcode=1+ echo as_func_ret_success failed.+fi++if as_func_ret_failure; then+ exitcode=1+ echo as_func_ret_failure succeeded.+fi++if ( set x; as_func_ret_success y && test x = \"\$1\" ); then+ :+else+ exitcode=1+ echo positional parameters were not saved.+fi++test \$exitcode = 0) || { (exit 1); exit 1; }++(+ as_lineno_1=\$LINENO+ as_lineno_2=\$LINENO+ test \"x\$as_lineno_1\" != \"x\$as_lineno_2\" &&+ test \"x\`expr \$as_lineno_1 + 1\`\" = \"x\$as_lineno_2\") || { (exit 1); exit 1; }+") 2> /dev/null; then+ :+else+ as_candidate_shells=+ as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ case $as_dir in+ /*)+ for as_base in sh bash ksh sh5; do+ as_candidate_shells="$as_candidate_shells $as_dir/$as_base"+ done;;+ esac+done+IFS=$as_save_IFS+++ for as_shell in $as_candidate_shells $SHELL; do+ # Try only shells that exist, to save several forks.+ if { test -f "$as_shell" || test -f "$as_shell.exe"; } &&+ { ("$as_shell") 2> /dev/null <<\_ASEOF+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then+ emulate sh+ NULLCMD=:+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which+ # is contrary to our usage. Disable this feature.+ alias -g '${1+"$@"}'='"$@"'+ setopt NO_GLOB_SUBST+else+ case `(set -o) 2>/dev/null` in+ *posix*) set -o posix ;;+esac++fi+++:+_ASEOF+}; then+ CONFIG_SHELL=$as_shell+ as_have_required=yes+ if { "$as_shell" 2> /dev/null <<\_ASEOF+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then+ emulate sh+ NULLCMD=:+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which+ # is contrary to our usage. Disable this feature.+ alias -g '${1+"$@"}'='"$@"'+ setopt NO_GLOB_SUBST+else+ case `(set -o) 2>/dev/null` in+ *posix*) set -o posix ;;+esac++fi+++:+(as_func_return () {+ (exit $1)+}+as_func_success () {+ as_func_return 0+}+as_func_failure () {+ as_func_return 1+}+as_func_ret_success () {+ return 0+}+as_func_ret_failure () {+ return 1+}++exitcode=0+if as_func_success; then+ :+else+ exitcode=1+ echo as_func_success failed.+fi++if as_func_failure; then+ exitcode=1+ echo as_func_failure succeeded.+fi++if as_func_ret_success; then+ :+else+ exitcode=1+ echo as_func_ret_success failed.+fi++if as_func_ret_failure; then+ exitcode=1+ echo as_func_ret_failure succeeded.+fi++if ( set x; as_func_ret_success y && test x = "$1" ); then+ :+else+ exitcode=1+ echo positional parameters were not saved.+fi++test $exitcode = 0) || { (exit 1); exit 1; }++(+ as_lineno_1=$LINENO+ as_lineno_2=$LINENO+ test "x$as_lineno_1" != "x$as_lineno_2" &&+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2") || { (exit 1); exit 1; }++_ASEOF+}; then+ break+fi++fi++ done++ if test "x$CONFIG_SHELL" != x; then+ for as_var in BASH_ENV ENV+ do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+ done+ export CONFIG_SHELL+ exec "$CONFIG_SHELL" "$as_myself" ${1+"$@"}+fi+++ if test $as_have_required = no; then+ echo This script requires a shell more modern than all the+ echo shells that I found on your system. Please install a+ echo modern shell, or manually run the script under such a+ echo shell if you do have one.+ { (exit 1); exit 1; }+fi+++fi++fi++++(eval "as_func_return () {+ (exit \$1)+}+as_func_success () {+ as_func_return 0+}+as_func_failure () {+ as_func_return 1+}+as_func_ret_success () {+ return 0+}+as_func_ret_failure () {+ return 1+}++exitcode=0+if as_func_success; then+ :+else+ exitcode=1+ echo as_func_success failed.+fi++if as_func_failure; then+ exitcode=1+ echo as_func_failure succeeded.+fi++if as_func_ret_success; then+ :+else+ exitcode=1+ echo as_func_ret_success failed.+fi++if as_func_ret_failure; then+ exitcode=1+ echo as_func_ret_failure succeeded.+fi++if ( set x; as_func_ret_success y && test x = \"\$1\" ); then+ :+else+ exitcode=1+ echo positional parameters were not saved.+fi++test \$exitcode = 0") || {+ echo No shell found that supports shell functions.+ echo Please tell autoconf@gnu.org about your system,+ echo including any error possibly output before this+ echo message+}++++ as_lineno_1=$LINENO+ as_lineno_2=$LINENO+ test "x$as_lineno_1" != "x$as_lineno_2" &&+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {++ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO+ # uniformly replaced by the line number. The first 'sed' inserts a+ # line-number line after each line using $LINENO; the second 'sed'+ # does the real work. The second script uses 'N' to pair each+ # line-number line with the line containing $LINENO, and appends+ # trailing '-' during substitution so that $LINENO is not a special+ # case at line end.+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the+ # scripts with optimization help from Paolo Bonzini. Blame Lee+ # E. McMahon (1931-1989) for sed's syntax. :-)+ sed -n '+ p+ /[$]LINENO/=+ ' <$as_myself |+ sed '+ s/[$]LINENO.*/&-/+ t lineno+ b+ :lineno+ N+ :loop+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/+ t loop+ s/-\n.*//+ ' >$as_me.lineno &&+ chmod +x "$as_me.lineno" ||+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2+ { (exit 1); exit 1; }; }++ # Don't try to exec as it changes $[0], causing all sort of problems+ # (the dirname of $[0] is not the place where we might find the+ # original and so on. Autoconf is especially sensitive to this).+ . "./$as_me.lineno"+ # Exit status is that of the last command.+ exit+}+++if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then+ as_dirname=dirname+else+ as_dirname=false+fi++ECHO_C= ECHO_N= ECHO_T=+case `echo -n x` in+-n*)+ case `echo 'x\c'` in+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.+ *) ECHO_C='\c';;+ esac;;+*)+ ECHO_N='-n';;+esac++if expr a : '\(a\)' >/dev/null 2>&1 &&+ test "X`expr 00001 : '.*\(...\)'`" = X001; then+ as_expr=expr+else+ as_expr=false+fi++rm -f conf$$ conf$$.exe conf$$.file+if test -d conf$$.dir; then+ rm -f conf$$.dir/conf$$.file+else+ rm -f conf$$.dir+ mkdir conf$$.dir+fi+echo >conf$$.file+if ln -s conf$$.file conf$$ 2>/dev/null; then+ as_ln_s='ln -s'+ # ... but there are two gotchas:+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.+ # In both cases, we have to default to `cp -p'.+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||+ as_ln_s='cp -p'+elif ln conf$$.file conf$$ 2>/dev/null; then+ as_ln_s=ln+else+ as_ln_s='cp -p'+fi+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file+rmdir conf$$.dir 2>/dev/null++if mkdir -p . 2>/dev/null; then+ as_mkdir_p=:+else+ test -d ./-p && rmdir ./-p+ as_mkdir_p=false+fi++if test -x / >/dev/null 2>&1; then+ as_test_x='test -x'+else+ if ls -dL / >/dev/null 2>&1; then+ as_ls_L_option=L+ else+ as_ls_L_option=+ fi+ as_test_x='+ eval sh -c '\''+ if test -d "$1"; then+ test -d "$1/.";+ else+ case $1 in+ -*)set "./$1";;+ esac;+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in+ ???[sx]*):;;*)false;;esac;fi+ '\'' sh+ '+fi+as_executable_p=$as_test_x++# Sed expression to map a string onto a valid CPP name.+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"++# Sed expression to map a string onto a valid variable name.+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"++++exec 7<&0 </dev/null 6>&1++# Name of the host.+# hostname on some systems (SVR3.2, Linux) returns a bogus exit status,+# so uname gets run too.+ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q`++#+# Initializations.+#+ac_default_prefix=/usr/local+ac_clean_files=+ac_config_libobj_dir=.+LIBOBJS=+cross_compiling=no+subdirs=+MFLAGS=+MAKEFLAGS=+SHELL=${CONFIG_SHELL-/bin/sh}++# Identity of this package.+PACKAGE_NAME='Haskell LevelDB package'+PACKAGE_TARNAME='leveldb'+PACKAGE_VERSION='0.1'+PACKAGE_STRING='Haskell LevelDB package 0.1'+PACKAGE_BUGREPORT='mad.one@gmail.com'++# Factoring default headers for most tests.+ac_includes_default="\+#include <stdio.h>+#ifdef HAVE_SYS_TYPES_H+# include <sys/types.h>+#endif+#ifdef HAVE_SYS_STAT_H+# include <sys/stat.h>+#endif+#ifdef STDC_HEADERS+# include <stdlib.h>+# include <stddef.h>+#else+# ifdef HAVE_STDLIB_H+# include <stdlib.h>+# endif+#endif+#ifdef HAVE_STRING_H+# if !defined STDC_HEADERS && defined HAVE_MEMORY_H+# include <memory.h>+# endif+# include <string.h>+#endif+#ifdef HAVE_STRINGS_H+# include <strings.h>+#endif+#ifdef HAVE_INTTYPES_H+# include <inttypes.h>+#endif+#ifdef HAVE_STDINT_H+# include <stdint.h>+#endif+#ifdef HAVE_UNISTD_H+# include <unistd.h>+#endif"++ac_subst_vars='SHELL+PATH_SEPARATOR+PACKAGE_NAME+PACKAGE_TARNAME+PACKAGE_VERSION+PACKAGE_STRING+PACKAGE_BUGREPORT+exec_prefix+prefix+program_transform_name+bindir+sbindir+libexecdir+datarootdir+datadir+sysconfdir+sharedstatedir+localstatedir+includedir+oldincludedir+docdir+infodir+htmldir+dvidir+pdfdir+psdir+libdir+localedir+mandir+DEFS+ECHO_C+ECHO_N+ECHO_T+LIBS+build_alias+host_alias+target_alias+CXX+CXXFLAGS+LDFLAGS+CPPFLAGS+ac_ct_CXX+EXEEXT+OBJEXT+CXXCPP+GREP+EGREP+SNAPPY+SNAPPYLIB+CSTDATOMIC+LIBOBJS+LTLIBOBJS'+ac_subst_files=''+ ac_precious_vars='build_alias+host_alias+target_alias+CXX+CXXFLAGS+LDFLAGS+LIBS+CPPFLAGS+CCC+CXXCPP'+++# Initialize some variables set by options.+ac_init_help=+ac_init_version=false+# The variables have the same names as the options, with+# dashes changed to underlines.+cache_file=/dev/null+exec_prefix=NONE+no_create=+no_recursion=+prefix=NONE+program_prefix=NONE+program_suffix=NONE+program_transform_name=s,x,x,+silent=+site=+srcdir=+verbose=+x_includes=NONE+x_libraries=NONE++# Installation directory options.+# These are left unexpanded so users can "make install exec_prefix=/foo"+# and all the variables that are supposed to be based on exec_prefix+# by default will actually change.+# Use braces instead of parens because sh, perl, etc. also accept them.+# (The list follows the same order as the GNU Coding Standards.)+bindir='${exec_prefix}/bin'+sbindir='${exec_prefix}/sbin'+libexecdir='${exec_prefix}/libexec'+datarootdir='${prefix}/share'+datadir='${datarootdir}'+sysconfdir='${prefix}/etc'+sharedstatedir='${prefix}/com'+localstatedir='${prefix}/var'+includedir='${prefix}/include'+oldincludedir='/usr/include'+docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'+infodir='${datarootdir}/info'+htmldir='${docdir}'+dvidir='${docdir}'+pdfdir='${docdir}'+psdir='${docdir}'+libdir='${exec_prefix}/lib'+localedir='${datarootdir}/locale'+mandir='${datarootdir}/man'++ac_prev=+ac_dashdash=+for ac_option+do+ # If the previous option needs an argument, assign it.+ if test -n "$ac_prev"; then+ eval $ac_prev=\$ac_option+ ac_prev=+ continue+ fi++ case $ac_option in+ *=*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;;+ *) ac_optarg=yes ;;+ esac++ # Accept the important Cygnus configure options, so we can diagnose typos.++ case $ac_dashdash$ac_option in+ --)+ ac_dashdash=yes ;;++ -bindir | --bindir | --bindi | --bind | --bin | --bi)+ ac_prev=bindir ;;+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)+ bindir=$ac_optarg ;;++ -build | --build | --buil | --bui | --bu)+ ac_prev=build_alias ;;+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)+ build_alias=$ac_optarg ;;++ -cache-file | --cache-file | --cache-fil | --cache-fi \+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)+ ac_prev=cache_file ;;+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)+ cache_file=$ac_optarg ;;++ --config-cache | -C)+ cache_file=config.cache ;;++ -datadir | --datadir | --datadi | --datad)+ ac_prev=datadir ;;+ -datadir=* | --datadir=* | --datadi=* | --datad=*)+ datadir=$ac_optarg ;;++ -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \+ | --dataroo | --dataro | --datar)+ ac_prev=datarootdir ;;+ -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \+ | --dataroot=* | --dataroo=* | --dataro=* | --datar=*)+ datarootdir=$ac_optarg ;;++ -disable-* | --disable-*)+ ac_feature=`expr "x$ac_option" : 'x-*disable-\(.*\)'`+ # Reject names that are not valid shell variable names.+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2+ { (exit 1); exit 1; }; }+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`+ eval enable_$ac_feature=no ;;++ -docdir | --docdir | --docdi | --doc | --do)+ ac_prev=docdir ;;+ -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*)+ docdir=$ac_optarg ;;++ -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv)+ ac_prev=dvidir ;;+ -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*)+ dvidir=$ac_optarg ;;++ -enable-* | --enable-*)+ ac_feature=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'`+ # Reject names that are not valid shell variable names.+ expr "x$ac_feature" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid feature name: $ac_feature" >&2+ { (exit 1); exit 1; }; }+ ac_feature=`echo $ac_feature | sed 's/[-.]/_/g'`+ eval enable_$ac_feature=\$ac_optarg ;;++ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \+ | --exec | --exe | --ex)+ ac_prev=exec_prefix ;;+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \+ | --exec=* | --exe=* | --ex=*)+ exec_prefix=$ac_optarg ;;++ -gas | --gas | --ga | --g)+ # Obsolete; use --with-gas.+ with_gas=yes ;;++ -help | --help | --hel | --he | -h)+ ac_init_help=long ;;+ -help=r* | --help=r* | --hel=r* | --he=r* | -hr*)+ ac_init_help=recursive ;;+ -help=s* | --help=s* | --hel=s* | --he=s* | -hs*)+ ac_init_help=short ;;++ -host | --host | --hos | --ho)+ ac_prev=host_alias ;;+ -host=* | --host=* | --hos=* | --ho=*)+ host_alias=$ac_optarg ;;++ -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht)+ ac_prev=htmldir ;;+ -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \+ | --ht=*)+ htmldir=$ac_optarg ;;++ -includedir | --includedir | --includedi | --included | --include \+ | --includ | --inclu | --incl | --inc)+ ac_prev=includedir ;;+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \+ | --includ=* | --inclu=* | --incl=* | --inc=*)+ includedir=$ac_optarg ;;++ -infodir | --infodir | --infodi | --infod | --info | --inf)+ ac_prev=infodir ;;+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)+ infodir=$ac_optarg ;;++ -libdir | --libdir | --libdi | --libd)+ ac_prev=libdir ;;+ -libdir=* | --libdir=* | --libdi=* | --libd=*)+ libdir=$ac_optarg ;;++ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \+ | --libexe | --libex | --libe)+ ac_prev=libexecdir ;;+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \+ | --libexe=* | --libex=* | --libe=*)+ libexecdir=$ac_optarg ;;++ -localedir | --localedir | --localedi | --localed | --locale)+ ac_prev=localedir ;;+ -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*)+ localedir=$ac_optarg ;;++ -localstatedir | --localstatedir | --localstatedi | --localstated \+ | --localstate | --localstat | --localsta | --localst | --locals)+ ac_prev=localstatedir ;;+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \+ | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*)+ localstatedir=$ac_optarg ;;++ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)+ ac_prev=mandir ;;+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)+ mandir=$ac_optarg ;;++ -nfp | --nfp | --nf)+ # Obsolete; use --without-fp.+ with_fp=no ;;++ -no-create | --no-create | --no-creat | --no-crea | --no-cre \+ | --no-cr | --no-c | -n)+ no_create=yes ;;++ -no-recursion | --no-recursion | --no-recursio | --no-recursi \+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)+ no_recursion=yes ;;++ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \+ | --oldin | --oldi | --old | --ol | --o)+ ac_prev=oldincludedir ;;+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)+ oldincludedir=$ac_optarg ;;++ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)+ ac_prev=prefix ;;+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)+ prefix=$ac_optarg ;;++ -program-prefix | --program-prefix | --program-prefi | --program-pref \+ | --program-pre | --program-pr | --program-p)+ ac_prev=program_prefix ;;+ -program-prefix=* | --program-prefix=* | --program-prefi=* \+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)+ program_prefix=$ac_optarg ;;++ -program-suffix | --program-suffix | --program-suffi | --program-suff \+ | --program-suf | --program-su | --program-s)+ ac_prev=program_suffix ;;+ -program-suffix=* | --program-suffix=* | --program-suffi=* \+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)+ program_suffix=$ac_optarg ;;++ -program-transform-name | --program-transform-name \+ | --program-transform-nam | --program-transform-na \+ | --program-transform-n | --program-transform- \+ | --program-transform | --program-transfor \+ | --program-transfo | --program-transf \+ | --program-trans | --program-tran \+ | --progr-tra | --program-tr | --program-t)+ ac_prev=program_transform_name ;;+ -program-transform-name=* | --program-transform-name=* \+ | --program-transform-nam=* | --program-transform-na=* \+ | --program-transform-n=* | --program-transform-=* \+ | --program-transform=* | --program-transfor=* \+ | --program-transfo=* | --program-transf=* \+ | --program-trans=* | --program-tran=* \+ | --progr-tra=* | --program-tr=* | --program-t=*)+ program_transform_name=$ac_optarg ;;++ -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd)+ ac_prev=pdfdir ;;+ -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*)+ pdfdir=$ac_optarg ;;++ -psdir | --psdir | --psdi | --psd | --ps)+ ac_prev=psdir ;;+ -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*)+ psdir=$ac_optarg ;;++ -q | -quiet | --quiet | --quie | --qui | --qu | --q \+ | -silent | --silent | --silen | --sile | --sil)+ silent=yes ;;++ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)+ ac_prev=sbindir ;;+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \+ | --sbi=* | --sb=*)+ sbindir=$ac_optarg ;;++ -sharedstatedir | --sharedstatedir | --sharedstatedi \+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \+ | --sharedst | --shareds | --shared | --share | --shar \+ | --sha | --sh)+ ac_prev=sharedstatedir ;;+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \+ | --sha=* | --sh=*)+ sharedstatedir=$ac_optarg ;;++ -site | --site | --sit)+ ac_prev=site ;;+ -site=* | --site=* | --sit=*)+ site=$ac_optarg ;;++ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)+ ac_prev=srcdir ;;+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)+ srcdir=$ac_optarg ;;++ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \+ | --syscon | --sysco | --sysc | --sys | --sy)+ ac_prev=sysconfdir ;;+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)+ sysconfdir=$ac_optarg ;;++ -target | --target | --targe | --targ | --tar | --ta | --t)+ ac_prev=target_alias ;;+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)+ target_alias=$ac_optarg ;;++ -v | -verbose | --verbose | --verbos | --verbo | --verb)+ verbose=yes ;;++ -version | --version | --versio | --versi | --vers | -V)+ ac_init_version=: ;;++ -with-* | --with-*)+ ac_package=`expr "x$ac_option" : 'x-*with-\([^=]*\)'`+ # Reject names that are not valid shell variable names.+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid package name: $ac_package" >&2+ { (exit 1); exit 1; }; }+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`+ eval with_$ac_package=\$ac_optarg ;;++ -without-* | --without-*)+ ac_package=`expr "x$ac_option" : 'x-*without-\(.*\)'`+ # Reject names that are not valid shell variable names.+ expr "x$ac_package" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid package name: $ac_package" >&2+ { (exit 1); exit 1; }; }+ ac_package=`echo $ac_package | sed 's/[-.]/_/g'`+ eval with_$ac_package=no ;;++ --x)+ # Obsolete; use --with-x.+ with_x=yes ;;++ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \+ | --x-incl | --x-inc | --x-in | --x-i)+ ac_prev=x_includes ;;+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)+ x_includes=$ac_optarg ;;++ -x-libraries | --x-libraries | --x-librarie | --x-librari \+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)+ ac_prev=x_libraries ;;+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)+ x_libraries=$ac_optarg ;;++ -*) { echo "$as_me: error: unrecognized option: $ac_option+Try \`$0 --help' for more information." >&2+ { (exit 1); exit 1; }; }+ ;;++ *=*)+ ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='`+ # Reject names that are not valid shell variable names.+ expr "x$ac_envvar" : ".*[^_$as_cr_alnum]" >/dev/null &&+ { echo "$as_me: error: invalid variable name: $ac_envvar" >&2+ { (exit 1); exit 1; }; }+ eval $ac_envvar=\$ac_optarg+ export $ac_envvar ;;++ *)+ # FIXME: should be removed in autoconf 3.0.+ echo "$as_me: WARNING: you should use --build, --host, --target" >&2+ expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null &&+ echo "$as_me: WARNING: invalid host type: $ac_option" >&2+ : ${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}+ ;;++ esac+done++if test -n "$ac_prev"; then+ ac_option=--`echo $ac_prev | sed 's/_/-/g'`+ { echo "$as_me: error: missing argument to $ac_option" >&2+ { (exit 1); exit 1; }; }+fi++# Be sure to have absolute directory names.+for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \+ datadir sysconfdir sharedstatedir localstatedir includedir \+ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \+ libdir localedir mandir+do+ eval ac_val=\$$ac_var+ case $ac_val in+ [\\/$]* | ?:[\\/]* ) continue;;+ NONE | '' ) case $ac_var in *prefix ) continue;; esac;;+ esac+ { echo "$as_me: error: expected an absolute directory name for --$ac_var: $ac_val" >&2+ { (exit 1); exit 1; }; }+done++# There might be people who depend on the old broken behavior: `$host'+# used to hold the argument of --host etc.+# FIXME: To remove some day.+build=$build_alias+host=$host_alias+target=$target_alias++# FIXME: To remove some day.+if test "x$host_alias" != x; then+ if test "x$build_alias" = x; then+ cross_compiling=maybe+ echo "$as_me: WARNING: If you wanted to set the --build type, don't use --host.+ If a cross compiler is detected then cross compile mode will be used." >&2+ elif test "x$build_alias" != "x$host_alias"; then+ cross_compiling=yes+ fi+fi++ac_tool_prefix=+test -n "$host_alias" && ac_tool_prefix=$host_alias-++test "$silent" = yes && exec 6>/dev/null+++ac_pwd=`pwd` && test -n "$ac_pwd" &&+ac_ls_di=`ls -di .` &&+ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` ||+ { echo "$as_me: error: Working directory cannot be determined" >&2+ { (exit 1); exit 1; }; }+test "X$ac_ls_di" = "X$ac_pwd_ls_di" ||+ { echo "$as_me: error: pwd does not report name of working directory" >&2+ { (exit 1); exit 1; }; }+++# Find the source files, if location was not specified.+if test -z "$srcdir"; then+ ac_srcdir_defaulted=yes+ # Try the directory containing this script, then the parent directory.+ ac_confdir=`$as_dirname -- "$0" ||+$as_expr X"$0" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \+ X"$0" : 'X\(//\)[^/]' \| \+ X"$0" : 'X\(//\)$' \| \+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||+echo X"$0" |+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{+ s//\1/+ q+ }+ /^X\(\/\/\)[^/].*/{+ s//\1/+ q+ }+ /^X\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`+ srcdir=$ac_confdir+ if test ! -r "$srcdir/$ac_unique_file"; then+ srcdir=..+ fi+else+ ac_srcdir_defaulted=no+fi+if test ! -r "$srcdir/$ac_unique_file"; then+ test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .."+ { echo "$as_me: error: cannot find sources ($ac_unique_file) in $srcdir" >&2+ { (exit 1); exit 1; }; }+fi+ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work"+ac_abs_confdir=`(+ cd "$srcdir" && test -r "./$ac_unique_file" || { echo "$as_me: error: $ac_msg" >&2+ { (exit 1); exit 1; }; }+ pwd)`+# When building in place, set srcdir=.+if test "$ac_abs_confdir" = "$ac_pwd"; then+ srcdir=.+fi+# Remove unnecessary trailing slashes from srcdir.+# Double slashes in file names in object file debugging info+# mess up M-x gdb in Emacs.+case $srcdir in+*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;;+esac+for ac_var in $ac_precious_vars; do+ eval ac_env_${ac_var}_set=\${${ac_var}+set}+ eval ac_env_${ac_var}_value=\$${ac_var}+ eval ac_cv_env_${ac_var}_set=\${${ac_var}+set}+ eval ac_cv_env_${ac_var}_value=\$${ac_var}+done++#+# Report the --help message.+#+if test "$ac_init_help" = "long"; then+ # Omit some internal or obsolete options to make the list less imposing.+ # This message is too long to be a string in the A/UX 3.1 sh.+ cat <<_ACEOF+\`configure' configures Haskell LevelDB package 0.1 to adapt to many kinds of systems.++Usage: $0 [OPTION]... [VAR=VALUE]...++To assign environment variables (e.g., CC, CFLAGS...), specify them as+VAR=VALUE. See below for descriptions of some of the useful variables.++Defaults for the options are specified in brackets.++Configuration:+ -h, --help display this help and exit+ --help=short display options specific to this package+ --help=recursive display the short help of all the included packages+ -V, --version display version information and exit+ -q, --quiet, --silent do not print \`checking...' messages+ --cache-file=FILE cache test results in FILE [disabled]+ -C, --config-cache alias for \`--cache-file=config.cache'+ -n, --no-create do not create output files+ --srcdir=DIR find the sources in DIR [configure dir or \`..']++Installation directories:+ --prefix=PREFIX install architecture-independent files in PREFIX+ [$ac_default_prefix]+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX+ [PREFIX]++By default, \`make install' will install all the files in+\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify+an installation prefix other than \`$ac_default_prefix' using \`--prefix',+for instance \`--prefix=\$HOME'.++For better control, use the options below.++Fine tuning of the installation directories:+ --bindir=DIR user executables [EPREFIX/bin]+ --sbindir=DIR system admin executables [EPREFIX/sbin]+ --libexecdir=DIR program executables [EPREFIX/libexec]+ --sysconfdir=DIR read-only single-machine data [PREFIX/etc]+ --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]+ --localstatedir=DIR modifiable single-machine data [PREFIX/var]+ --libdir=DIR object code libraries [EPREFIX/lib]+ --includedir=DIR C header files [PREFIX/include]+ --oldincludedir=DIR C header files for non-gcc [/usr/include]+ --datarootdir=DIR read-only arch.-independent data root [PREFIX/share]+ --datadir=DIR read-only architecture-independent data [DATAROOTDIR]+ --infodir=DIR info documentation [DATAROOTDIR/info]+ --localedir=DIR locale-dependent data [DATAROOTDIR/locale]+ --mandir=DIR man documentation [DATAROOTDIR/man]+ --docdir=DIR documentation root [DATAROOTDIR/doc/leveldb]+ --htmldir=DIR html documentation [DOCDIR]+ --dvidir=DIR dvi documentation [DOCDIR]+ --pdfdir=DIR pdf documentation [DOCDIR]+ --psdir=DIR ps documentation [DOCDIR]+_ACEOF++ cat <<\_ACEOF+_ACEOF+fi++if test -n "$ac_init_help"; then+ case $ac_init_help in+ short | recursive ) echo "Configuration of Haskell LevelDB package 0.1:";;+ esac+ cat <<\_ACEOF++Some influential environment variables:+ CXX C++ compiler command+ CXXFLAGS C++ compiler flags+ LDFLAGS linker flags, e.g. -L<lib dir> if you have libraries in a+ nonstandard directory <lib dir>+ LIBS libraries to pass to the linker, e.g. -l<library>+ CPPFLAGS C/C++/Objective C preprocessor flags, e.g. -I<include dir> if+ you have headers in a nonstandard directory <include dir>+ CXXCPP C++ preprocessor++Use these variables to override the choices made by `configure' or to help+it to find libraries and programs with nonstandard names/locations.++Report bugs to <mad.one@gmail.com>.+_ACEOF+ac_status=$?+fi++if test "$ac_init_help" = "recursive"; then+ # If there are subdirs, report their specific --help.+ for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue+ test -d "$ac_dir" || continue+ ac_builddir=.++case "$ac_dir" in+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;+*)+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`+ # A ".." for each directory in $ac_dir_suffix.+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`+ case $ac_top_builddir_sub in+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;+ esac ;;+esac+ac_abs_top_builddir=$ac_pwd+ac_abs_builddir=$ac_pwd$ac_dir_suffix+# for backward compatibility:+ac_top_builddir=$ac_top_build_prefix++case $srcdir in+ .) # We are building in place.+ ac_srcdir=.+ ac_top_srcdir=$ac_top_builddir_sub+ ac_abs_top_srcdir=$ac_pwd ;;+ [\\/]* | ?:[\\/]* ) # Absolute name.+ ac_srcdir=$srcdir$ac_dir_suffix;+ ac_top_srcdir=$srcdir+ ac_abs_top_srcdir=$srcdir ;;+ *) # Relative name.+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix+ ac_top_srcdir=$ac_top_build_prefix$srcdir+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;+esac+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix++ cd "$ac_dir" || { ac_status=$?; continue; }+ # Check for guested configure.+ if test -f "$ac_srcdir/configure.gnu"; then+ echo &&+ $SHELL "$ac_srcdir/configure.gnu" --help=recursive+ elif test -f "$ac_srcdir/configure"; then+ echo &&+ $SHELL "$ac_srcdir/configure" --help=recursive+ else+ echo "$as_me: WARNING: no configuration information is in $ac_dir" >&2+ fi || ac_status=$?+ cd "$ac_pwd" || { ac_status=$?; break; }+ done+fi++test -n "$ac_init_help" && exit $ac_status+if $ac_init_version; then+ cat <<\_ACEOF+Haskell LevelDB package configure 0.1+generated by GNU Autoconf 2.61++Copyright (C) 1992, 1993, 1994, 1995, 1996, 1998, 1999, 2000, 2001,+2002, 2003, 2004, 2005, 2006 Free Software Foundation, Inc.+This configure script is free software; the Free Software Foundation+gives unlimited permission to copy, distribute and modify it.+_ACEOF+ exit+fi+cat >config.log <<_ACEOF+This file contains any messages produced by compilers while+running configure, to aid debugging if configure makes a mistake.++It was created by Haskell LevelDB package $as_me 0.1, which was+generated by GNU Autoconf 2.61. Invocation command line was++ $ $0 $@++_ACEOF+exec 5>>config.log+{+cat <<_ASUNAME+## --------- ##+## Platform. ##+## --------- ##++hostname = `(hostname || uname -n) 2>/dev/null | sed 1q`+uname -m = `(uname -m) 2>/dev/null || echo unknown`+uname -r = `(uname -r) 2>/dev/null || echo unknown`+uname -s = `(uname -s) 2>/dev/null || echo unknown`+uname -v = `(uname -v) 2>/dev/null || echo unknown`++/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown`+/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown`++/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown`+/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown`+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown`+/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown`+/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown`+/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown`+/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown`++_ASUNAME++as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ echo "PATH: $as_dir"+done+IFS=$as_save_IFS++} >&5++cat >&5 <<_ACEOF+++## ----------- ##+## Core tests. ##+## ----------- ##++_ACEOF+++# Keep a trace of the command line.+# Strip out --no-create and --no-recursion so they do not pile up.+# Strip out --silent because we don't want to record it for future runs.+# Also quote any args containing shell meta-characters.+# Make two passes to allow for proper duplicate-argument suppression.+ac_configure_args=+ac_configure_args0=+ac_configure_args1=+ac_must_keep_next=false+for ac_pass in 1 2+do+ for ac_arg+ do+ case $ac_arg in+ -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;;+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \+ | -silent | --silent | --silen | --sile | --sil)+ continue ;;+ *\'*)+ ac_arg=`echo "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;;+ esac+ case $ac_pass in+ 1) ac_configure_args0="$ac_configure_args0 '$ac_arg'" ;;+ 2)+ ac_configure_args1="$ac_configure_args1 '$ac_arg'"+ if test $ac_must_keep_next = true; then+ ac_must_keep_next=false # Got value, back to normal.+ else+ case $ac_arg in+ *=* | --config-cache | -C | -disable-* | --disable-* \+ | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \+ | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \+ | -with-* | --with-* | -without-* | --without-* | --x)+ case "$ac_configure_args0 " in+ "$ac_configure_args1"*" '$ac_arg' "* ) continue ;;+ esac+ ;;+ -* ) ac_must_keep_next=true ;;+ esac+ fi+ ac_configure_args="$ac_configure_args '$ac_arg'"+ ;;+ esac+ done+done+$as_unset ac_configure_args0 || test "${ac_configure_args0+set}" != set || { ac_configure_args0=; export ac_configure_args0; }+$as_unset ac_configure_args1 || test "${ac_configure_args1+set}" != set || { ac_configure_args1=; export ac_configure_args1; }++# When interrupted or exit'd, cleanup temporary files, and complete+# config.log. We remove comments because anyway the quotes in there+# would cause problems or look ugly.+# WARNING: Use '\'' to represent an apostrophe within the trap.+# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug.+trap 'exit_status=$?+ # Save into config.log some information that might help in debugging.+ {+ echo++ cat <<\_ASBOX+## ---------------- ##+## Cache variables. ##+## ---------------- ##+_ASBOX+ echo+ # The following way of writing the cache mishandles newlines in values,+(+ for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do+ eval ac_val=\$$ac_var+ case $ac_val in #(+ *${as_nl}*)+ case $ac_var in #(+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;+ esac+ case $ac_var in #(+ _ | IFS | as_nl) ;; #(+ *) $as_unset $ac_var ;;+ esac ;;+ esac+ done+ (set) 2>&1 |+ case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #(+ *${as_nl}ac_space=\ *)+ sed -n \+ "s/'\''/'\''\\\\'\'''\''/g;+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p"+ ;; #(+ *)+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"+ ;;+ esac |+ sort+)+ echo++ cat <<\_ASBOX+## ----------------- ##+## Output variables. ##+## ----------------- ##+_ASBOX+ echo+ for ac_var in $ac_subst_vars+ do+ eval ac_val=\$$ac_var+ case $ac_val in+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;+ esac+ echo "$ac_var='\''$ac_val'\''"+ done | sort+ echo++ if test -n "$ac_subst_files"; then+ cat <<\_ASBOX+## ------------------- ##+## File substitutions. ##+## ------------------- ##+_ASBOX+ echo+ for ac_var in $ac_subst_files+ do+ eval ac_val=\$$ac_var+ case $ac_val in+ *\'\''*) ac_val=`echo "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;;+ esac+ echo "$ac_var='\''$ac_val'\''"+ done | sort+ echo+ fi++ if test -s confdefs.h; then+ cat <<\_ASBOX+## ----------- ##+## confdefs.h. ##+## ----------- ##+_ASBOX+ echo+ cat confdefs.h+ echo+ fi+ test "$ac_signal" != 0 &&+ echo "$as_me: caught signal $ac_signal"+ echo "$as_me: exit $exit_status"+ } >&5+ rm -f core *.core core.conftest.* &&+ rm -f -r conftest* confdefs* conf$$* $ac_clean_files &&+ exit $exit_status+' 0+for ac_signal in 1 2 13 15; do+ trap 'ac_signal='$ac_signal'; { (exit 1); exit 1; }' $ac_signal+done+ac_signal=0++# confdefs.h avoids OS command line length limits that DEFS can exceed.+rm -f -r conftest* confdefs.h++# Predefined preprocessor variables.++cat >>confdefs.h <<_ACEOF+#define PACKAGE_NAME "$PACKAGE_NAME"+_ACEOF+++cat >>confdefs.h <<_ACEOF+#define PACKAGE_TARNAME "$PACKAGE_TARNAME"+_ACEOF+++cat >>confdefs.h <<_ACEOF+#define PACKAGE_VERSION "$PACKAGE_VERSION"+_ACEOF+++cat >>confdefs.h <<_ACEOF+#define PACKAGE_STRING "$PACKAGE_STRING"+_ACEOF+++cat >>confdefs.h <<_ACEOF+#define PACKAGE_BUGREPORT "$PACKAGE_BUGREPORT"+_ACEOF+++# Let the site file select an alternate cache file if it wants to.+# Prefer explicitly selected file to automatically selected ones.+if test -n "$CONFIG_SITE"; then+ set x "$CONFIG_SITE"+elif test "x$prefix" != xNONE; then+ set x "$prefix/share/config.site" "$prefix/etc/config.site"+else+ set x "$ac_default_prefix/share/config.site" \+ "$ac_default_prefix/etc/config.site"+fi+shift+for ac_site_file+do+ if test -r "$ac_site_file"; then+ { echo "$as_me:$LINENO: loading site script $ac_site_file" >&5+echo "$as_me: loading site script $ac_site_file" >&6;}+ sed 's/^/| /' "$ac_site_file" >&5+ . "$ac_site_file"+ fi+done++if test -r "$cache_file"; then+ # Some versions of bash will fail to source /dev/null (special+ # files actually), so we avoid doing that.+ if test -f "$cache_file"; then+ { echo "$as_me:$LINENO: loading cache $cache_file" >&5+echo "$as_me: loading cache $cache_file" >&6;}+ case $cache_file in+ [\\/]* | ?:[\\/]* ) . "$cache_file";;+ *) . "./$cache_file";;+ esac+ fi+else+ { echo "$as_me:$LINENO: creating cache $cache_file" >&5+echo "$as_me: creating cache $cache_file" >&6;}+ >$cache_file+fi++# Check that the precious variables saved in the cache have kept the same+# value.+ac_cache_corrupted=false+for ac_var in $ac_precious_vars; do+ eval ac_old_set=\$ac_cv_env_${ac_var}_set+ eval ac_new_set=\$ac_env_${ac_var}_set+ eval ac_old_val=\$ac_cv_env_${ac_var}_value+ eval ac_new_val=\$ac_env_${ac_var}_value+ case $ac_old_set,$ac_new_set in+ set,)+ { echo "$as_me:$LINENO: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5+echo "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;}+ ac_cache_corrupted=: ;;+ ,set)+ { echo "$as_me:$LINENO: error: \`$ac_var' was not set in the previous run" >&5+echo "$as_me: error: \`$ac_var' was not set in the previous run" >&2;}+ ac_cache_corrupted=: ;;+ ,);;+ *)+ if test "x$ac_old_val" != "x$ac_new_val"; then+ { echo "$as_me:$LINENO: error: \`$ac_var' has changed since the previous run:" >&5+echo "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;}+ { echo "$as_me:$LINENO: former value: $ac_old_val" >&5+echo "$as_me: former value: $ac_old_val" >&2;}+ { echo "$as_me:$LINENO: current value: $ac_new_val" >&5+echo "$as_me: current value: $ac_new_val" >&2;}+ ac_cache_corrupted=:+ fi;;+ esac+ # Pass precious variables to config.status.+ if test "$ac_new_set" = set; then+ case $ac_new_val in+ *\'*) ac_arg=$ac_var=`echo "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;;+ *) ac_arg=$ac_var=$ac_new_val ;;+ esac+ case " $ac_configure_args " in+ *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy.+ *) ac_configure_args="$ac_configure_args '$ac_arg'" ;;+ esac+ fi+done+if $ac_cache_corrupted; then+ { echo "$as_me:$LINENO: error: changes in the environment can compromise the build" >&5+echo "$as_me: error: changes in the environment can compromise the build" >&2;}+ { { echo "$as_me:$LINENO: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&5+echo "$as_me: error: run \`make distclean' and/or \`rm $cache_file' and start over" >&2;}+ { (exit 1); exit 1; }; }+fi++++++++++++++++++++++++++ac_ext=c+ac_cpp='$CPP $CPPFLAGS'+ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_c_compiler_gnu++++ac_ext=cpp+ac_cpp='$CXXCPP $CPPFLAGS'+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu++++ac_ext=cpp+ac_cpp='$CXXCPP $CPPFLAGS'+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu+if test -z "$CXX"; then+ if test -n "$CCC"; then+ CXX=$CCC+ else+ if test -n "$ac_tool_prefix"; then+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC+ do+ # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args.+set dummy $ac_tool_prefix$ac_prog; ac_word=$2+{ echo "$as_me:$LINENO: checking for $ac_word" >&5+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }+if test "${ac_cv_prog_CXX+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test -n "$CXX"; then+ ac_cv_prog_CXX="$CXX" # Let the user override the test.+else+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_exec_ext in '' $ac_executable_extensions; do+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then+ ac_cv_prog_CXX="$ac_tool_prefix$ac_prog"+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5+ break 2+ fi+done+done+IFS=$as_save_IFS++fi+fi+CXX=$ac_cv_prog_CXX+if test -n "$CXX"; then+ { echo "$as_me:$LINENO: result: $CXX" >&5+echo "${ECHO_T}$CXX" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+++ test -n "$CXX" && break+ done+fi+if test -z "$CXX"; then+ ac_ct_CXX=$CXX+ for ac_prog in g++ c++ gpp aCC CC cxx cc++ cl.exe FCC KCC RCC xlC_r xlC+do+ # Extract the first word of "$ac_prog", so it can be a program name with args.+set dummy $ac_prog; ac_word=$2+{ echo "$as_me:$LINENO: checking for $ac_word" >&5+echo $ECHO_N "checking for $ac_word... $ECHO_C" >&6; }+if test "${ac_cv_prog_ac_ct_CXX+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if test -n "$ac_ct_CXX"; then+ ac_cv_prog_ac_ct_CXX="$ac_ct_CXX" # Let the user override the test.+else+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_exec_ext in '' $ac_executable_extensions; do+ if { test -f "$as_dir/$ac_word$ac_exec_ext" && $as_test_x "$as_dir/$ac_word$ac_exec_ext"; }; then+ ac_cv_prog_ac_ct_CXX="$ac_prog"+ echo "$as_me:$LINENO: found $as_dir/$ac_word$ac_exec_ext" >&5+ break 2+ fi+done+done+IFS=$as_save_IFS++fi+fi+ac_ct_CXX=$ac_cv_prog_ac_ct_CXX+if test -n "$ac_ct_CXX"; then+ { echo "$as_me:$LINENO: result: $ac_ct_CXX" >&5+echo "${ECHO_T}$ac_ct_CXX" >&6; }+else+ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+fi+++ test -n "$ac_ct_CXX" && break+done++ if test "x$ac_ct_CXX" = x; then+ CXX="g++"+ else+ case $cross_compiling:$ac_tool_warned in+yes:)+{ echo "$as_me:$LINENO: WARNING: In the future, Autoconf will not detect cross-tools+whose name does not start with the host triplet. If you think this+configuration is useful to you, please write to autoconf@gnu.org." >&5+echo "$as_me: WARNING: In the future, Autoconf will not detect cross-tools+whose name does not start with the host triplet. If you think this+configuration is useful to you, please write to autoconf@gnu.org." >&2;}+ac_tool_warned=yes ;;+esac+ CXX=$ac_ct_CXX+ fi+fi++ fi+fi+# Provide some information about the compiler.+echo "$as_me:$LINENO: checking for C++ compiler version" >&5+ac_compiler=`set X $ac_compile; echo $2`+{ (ac_try="$ac_compiler --version >&5"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compiler --version >&5") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }+{ (ac_try="$ac_compiler -v >&5"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compiler -v >&5") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }+{ (ac_try="$ac_compiler -V >&5"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compiler -V >&5") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }++cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+ac_clean_files_save=$ac_clean_files+ac_clean_files="$ac_clean_files a.out a.exe b.out"+# Try to create an executable without -o first, disregard a.out.+# It will help us diagnose broken compilers, and finding out an intuition+# of exeext.+{ echo "$as_me:$LINENO: checking for C++ compiler default output file name" >&5+echo $ECHO_N "checking for C++ compiler default output file name... $ECHO_C" >&6; }+ac_link_default=`echo "$ac_link" | sed 's/ -o *conftest[^ ]*//'`+#+# List of possible output files, starting from the most likely.+# The algorithm is not robust to junk in `.', hence go to wildcards (a.*)+# only as a last resort. b.out is created by i960 compilers.+ac_files='a_out.exe a.exe conftest.exe a.out conftest a.* conftest.* b.out'+#+# The IRIX 6 linker writes into existing files which may not be+# executable, retaining their permissions. Remove them first so a+# subsequent execution test works.+ac_rmfiles=+for ac_file in $ac_files+do+ case $ac_file in+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;+ * ) ac_rmfiles="$ac_rmfiles $ac_file";;+ esac+done+rm -f $ac_rmfiles++if { (ac_try="$ac_link_default"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link_default") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; then+ # Autoconf-2.13 could set the ac_cv_exeext variable to `no'.+# So ignore a value of `no', otherwise this would lead to `EXEEXT = no'+# in a Makefile. We should not override ac_cv_exeext if it was cached,+# so that the user can short-circuit this test for compilers unknown to+# Autoconf.+for ac_file in $ac_files ''+do+ test -f "$ac_file" || continue+ case $ac_file in+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj )+ ;;+ [ab].out )+ # We found the default executable, but exeext='' is most+ # certainly right.+ break;;+ *.* )+ if test "${ac_cv_exeext+set}" = set && test "$ac_cv_exeext" != no;+ then :; else+ ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`+ fi+ # We set ac_cv_exeext here because the later test for it is not+ # safe: cross compilers may not add the suffix if given an `-o'+ # argument, so we may need to know it at that point already.+ # Even if this section looks crufty: it has the advantage of+ # actually working.+ break;;+ * )+ break;;+ esac+done+test "$ac_cv_exeext" = no && ac_cv_exeext=++else+ ac_file=''+fi++{ echo "$as_me:$LINENO: result: $ac_file" >&5+echo "${ECHO_T}$ac_file" >&6; }+if test -z "$ac_file"; then+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++{ { echo "$as_me:$LINENO: error: C++ compiler cannot create executables+See \`config.log' for more details." >&5+echo "$as_me: error: C++ compiler cannot create executables+See \`config.log' for more details." >&2;}+ { (exit 77); exit 77; }; }+fi++ac_exeext=$ac_cv_exeext++# Check that the compiler produces executables we can run. If not, either+# the compiler is broken, or we cross compile.+{ echo "$as_me:$LINENO: checking whether the C++ compiler works" >&5+echo $ECHO_N "checking whether the C++ compiler works... $ECHO_C" >&6; }+# FIXME: These cross compiler hacks should be removed for Autoconf 3.0+# If not cross compiling, check that we can run a simple program.+if test "$cross_compiling" != yes; then+ if { ac_try='./$ac_file'+ { (case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_try") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; }; then+ cross_compiling=no+ else+ if test "$cross_compiling" = maybe; then+ cross_compiling=yes+ else+ { { echo "$as_me:$LINENO: error: cannot run C++ compiled programs.+If you meant to cross compile, use \`--host'.+See \`config.log' for more details." >&5+echo "$as_me: error: cannot run C++ compiled programs.+If you meant to cross compile, use \`--host'.+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }+ fi+ fi+fi+{ echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }++rm -f a.out a.exe conftest$ac_cv_exeext b.out+ac_clean_files=$ac_clean_files_save+# Check that the compiler produces executables we can run. If not, either+# the compiler is broken, or we cross compile.+{ echo "$as_me:$LINENO: checking whether we are cross compiling" >&5+echo $ECHO_N "checking whether we are cross compiling... $ECHO_C" >&6; }+{ echo "$as_me:$LINENO: result: $cross_compiling" >&5+echo "${ECHO_T}$cross_compiling" >&6; }++{ echo "$as_me:$LINENO: checking for suffix of executables" >&5+echo $ECHO_N "checking for suffix of executables... $ECHO_C" >&6; }+if { (ac_try="$ac_link"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; then+ # If both `conftest.exe' and `conftest' are `present' (well, observable)+# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will+# work properly (i.e., refer to `conftest.exe'), while it won't with+# `rm'.+for ac_file in conftest.exe conftest conftest.*; do+ test -f "$ac_file" || continue+ case $ac_file in+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.o | *.obj ) ;;+ *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'`+ break;;+ * ) break;;+ esac+done+else+ { { echo "$as_me:$LINENO: error: cannot compute suffix of executables: cannot compile and link+See \`config.log' for more details." >&5+echo "$as_me: error: cannot compute suffix of executables: cannot compile and link+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }+fi++rm -f conftest$ac_cv_exeext+{ echo "$as_me:$LINENO: result: $ac_cv_exeext" >&5+echo "${ECHO_T}$ac_cv_exeext" >&6; }++rm -f conftest.$ac_ext+EXEEXT=$ac_cv_exeext+ac_exeext=$EXEEXT+{ echo "$as_me:$LINENO: checking for suffix of object files" >&5+echo $ECHO_N "checking for suffix of object files... $ECHO_C" >&6; }+if test "${ac_cv_objext+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.o conftest.obj+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; then+ for ac_file in conftest.o conftest.obj conftest.*; do+ test -f "$ac_file" || continue;+ case $ac_file in+ *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf ) ;;+ *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'`+ break;;+ esac+done+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++{ { echo "$as_me:$LINENO: error: cannot compute suffix of object files: cannot compile+See \`config.log' for more details." >&5+echo "$as_me: error: cannot compute suffix of object files: cannot compile+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }+fi++rm -f conftest.$ac_cv_objext conftest.$ac_ext+fi+{ echo "$as_me:$LINENO: result: $ac_cv_objext" >&5+echo "${ECHO_T}$ac_cv_objext" >&6; }+OBJEXT=$ac_cv_objext+ac_objext=$OBJEXT+{ echo "$as_me:$LINENO: checking whether we are using the GNU C++ compiler" >&5+echo $ECHO_N "checking whether we are using the GNU C++ compiler... $ECHO_C" >&6; }+if test "${ac_cv_cxx_compiler_gnu+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{+#ifndef __GNUC__+ choke me+#endif++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_compiler_gnu=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_compiler_gnu=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+ac_cv_cxx_compiler_gnu=$ac_compiler_gnu++fi+{ echo "$as_me:$LINENO: result: $ac_cv_cxx_compiler_gnu" >&5+echo "${ECHO_T}$ac_cv_cxx_compiler_gnu" >&6; }+GXX=`test $ac_compiler_gnu = yes && echo yes`+ac_test_CXXFLAGS=${CXXFLAGS+set}+ac_save_CXXFLAGS=$CXXFLAGS+{ echo "$as_me:$LINENO: checking whether $CXX accepts -g" >&5+echo $ECHO_N "checking whether $CXX accepts -g... $ECHO_C" >&6; }+if test "${ac_cv_prog_cxx_g+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_save_cxx_werror_flag=$ac_cxx_werror_flag+ ac_cxx_werror_flag=yes+ ac_cv_prog_cxx_g=no+ CXXFLAGS="-g"+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_prog_cxx_g=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ CXXFLAGS=""+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ :+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cxx_werror_flag=$ac_save_cxx_werror_flag+ CXXFLAGS="-g"+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_prog_cxx_g=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5+++fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+ ac_cxx_werror_flag=$ac_save_cxx_werror_flag+fi+{ echo "$as_me:$LINENO: result: $ac_cv_prog_cxx_g" >&5+echo "${ECHO_T}$ac_cv_prog_cxx_g" >&6; }+if test "$ac_test_CXXFLAGS" = set; then+ CXXFLAGS=$ac_save_CXXFLAGS+elif test $ac_cv_prog_cxx_g = yes; then+ if test "$GXX" = yes; then+ CXXFLAGS="-g -O2"+ else+ CXXFLAGS="-g"+ fi+else+ if test "$GXX" = yes; then+ CXXFLAGS="-O2"+ else+ CXXFLAGS=+ fi+fi+ac_ext=cpp+ac_cpp='$CXXCPP $CPPFLAGS'+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu+++ac_ext=cpp+ac_cpp='$CXXCPP $CPPFLAGS'+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu+{ echo "$as_me:$LINENO: checking how to run the C++ preprocessor" >&5+echo $ECHO_N "checking how to run the C++ preprocessor... $ECHO_C" >&6; }+if test -z "$CXXCPP"; then+ if test "${ac_cv_prog_CXXCPP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ # Double quotes because CXXCPP needs to be expanded+ for CXXCPP in "$CXX -E" "/lib/cpp"+ do+ ac_preproc_ok=false+for ac_cxx_preproc_warn_flag in '' yes+do+ # Use a header file that comes with gcc, so configuring glibc+ # with a fresh cross-compiler works.+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since+ # <limits.h> exists even on freestanding compilers.+ # On the NeXT, cc -E runs the code through the compiler's parser,+ # not just through cpp. "Syntax error" is here to catch this case.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#ifdef __STDC__+# include <limits.h>+#else+# include <assert.h>+#endif+ Syntax error+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ }; then+ :+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ # Broken: fails on valid input.+continue+fi++rm -f conftest.err conftest.$ac_ext++ # OK, works on sane cases. Now check whether nonexistent headers+ # can be detected and how.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <ac_nonexistent.h>+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ }; then+ # Broken: success on invalid input.+continue+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ # Passes both tests.+ac_preproc_ok=:+break+fi++rm -f conftest.err conftest.$ac_ext++done+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.+rm -f conftest.err conftest.$ac_ext+if $ac_preproc_ok; then+ break+fi++ done+ ac_cv_prog_CXXCPP=$CXXCPP++fi+ CXXCPP=$ac_cv_prog_CXXCPP+else+ ac_cv_prog_CXXCPP=$CXXCPP+fi+{ echo "$as_me:$LINENO: result: $CXXCPP" >&5+echo "${ECHO_T}$CXXCPP" >&6; }+ac_preproc_ok=false+for ac_cxx_preproc_warn_flag in '' yes+do+ # Use a header file that comes with gcc, so configuring glibc+ # with a fresh cross-compiler works.+ # Prefer <limits.h> to <assert.h> if __STDC__ is defined, since+ # <limits.h> exists even on freestanding compilers.+ # On the NeXT, cc -E runs the code through the compiler's parser,+ # not just through cpp. "Syntax error" is here to catch this case.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#ifdef __STDC__+# include <limits.h>+#else+# include <assert.h>+#endif+ Syntax error+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ }; then+ :+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ # Broken: fails on valid input.+continue+fi++rm -f conftest.err conftest.$ac_ext++ # OK, works on sane cases. Now check whether nonexistent headers+ # can be detected and how.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <ac_nonexistent.h>+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ }; then+ # Broken: success on invalid input.+continue+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ # Passes both tests.+ac_preproc_ok=:+break+fi++rm -f conftest.err conftest.$ac_ext++done+# Because of `break', _AC_PREPROC_IFELSE's cleaning code was skipped.+rm -f conftest.err conftest.$ac_ext+if $ac_preproc_ok; then+ :+else+ { { echo "$as_me:$LINENO: error: C++ preprocessor \"$CXXCPP\" fails sanity check+See \`config.log' for more details." >&5+echo "$as_me: error: C++ preprocessor \"$CXXCPP\" fails sanity check+See \`config.log' for more details." >&2;}+ { (exit 1); exit 1; }; }+fi++ac_ext=cpp+ac_cpp='$CXXCPP $CPPFLAGS'+ac_compile='$CXX -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext >&5'+ac_link='$CXX -o conftest$ac_exeext $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5'+ac_compiler_gnu=$ac_cv_cxx_compiler_gnu+++{ echo "$as_me:$LINENO: checking for grep that handles long lines and -e" >&5+echo $ECHO_N "checking for grep that handles long lines and -e... $ECHO_C" >&6; }+if test "${ac_cv_path_GREP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ # Extract the first word of "grep ggrep" to use in msg output+if test -z "$GREP"; then+set dummy grep ggrep; ac_prog_name=$2+if test "${ac_cv_path_GREP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_path_GREP_found=false+# Loop through the user's path and test for each of PROGNAME-LIST+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_prog in grep ggrep; do+ for ac_exec_ext in '' $ac_executable_extensions; do+ ac_path_GREP="$as_dir/$ac_prog$ac_exec_ext"+ { test -f "$ac_path_GREP" && $as_test_x "$ac_path_GREP"; } || continue+ # Check for GNU ac_path_GREP and select it if it is found.+ # Check for GNU $ac_path_GREP+case `"$ac_path_GREP" --version 2>&1` in+*GNU*)+ ac_cv_path_GREP="$ac_path_GREP" ac_path_GREP_found=:;;+*)+ ac_count=0+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"+ while :+ do+ cat "conftest.in" "conftest.in" >"conftest.tmp"+ mv "conftest.tmp" "conftest.in"+ cp "conftest.in" "conftest.nl"+ echo 'GREP' >> "conftest.nl"+ "$ac_path_GREP" -e 'GREP$' -e '-(cannot match)-' < "conftest.nl" >"conftest.out" 2>/dev/null || break+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break+ ac_count=`expr $ac_count + 1`+ if test $ac_count -gt ${ac_path_GREP_max-0}; then+ # Best one so far, save it but keep looking for a better one+ ac_cv_path_GREP="$ac_path_GREP"+ ac_path_GREP_max=$ac_count+ fi+ # 10*(2^10) chars as input seems more than enough+ test $ac_count -gt 10 && break+ done+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;+esac+++ $ac_path_GREP_found && break 3+ done+done++done+IFS=$as_save_IFS+++fi++GREP="$ac_cv_path_GREP"+if test -z "$GREP"; then+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}+ { (exit 1); exit 1; }; }+fi++else+ ac_cv_path_GREP=$GREP+fi+++fi+{ echo "$as_me:$LINENO: result: $ac_cv_path_GREP" >&5+echo "${ECHO_T}$ac_cv_path_GREP" >&6; }+ GREP="$ac_cv_path_GREP"+++{ echo "$as_me:$LINENO: checking for egrep" >&5+echo $ECHO_N "checking for egrep... $ECHO_C" >&6; }+if test "${ac_cv_path_EGREP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ if echo a | $GREP -E '(a|b)' >/dev/null 2>&1+ then ac_cv_path_EGREP="$GREP -E"+ else+ # Extract the first word of "egrep" to use in msg output+if test -z "$EGREP"; then+set dummy egrep; ac_prog_name=$2+if test "${ac_cv_path_EGREP+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ ac_path_EGREP_found=false+# Loop through the user's path and test for each of PROGNAME-LIST+as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH$PATH_SEPARATOR/usr/xpg4/bin+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ for ac_prog in egrep; do+ for ac_exec_ext in '' $ac_executable_extensions; do+ ac_path_EGREP="$as_dir/$ac_prog$ac_exec_ext"+ { test -f "$ac_path_EGREP" && $as_test_x "$ac_path_EGREP"; } || continue+ # Check for GNU ac_path_EGREP and select it if it is found.+ # Check for GNU $ac_path_EGREP+case `"$ac_path_EGREP" --version 2>&1` in+*GNU*)+ ac_cv_path_EGREP="$ac_path_EGREP" ac_path_EGREP_found=:;;+*)+ ac_count=0+ echo $ECHO_N "0123456789$ECHO_C" >"conftest.in"+ while :+ do+ cat "conftest.in" "conftest.in" >"conftest.tmp"+ mv "conftest.tmp" "conftest.in"+ cp "conftest.in" "conftest.nl"+ echo 'EGREP' >> "conftest.nl"+ "$ac_path_EGREP" 'EGREP$' < "conftest.nl" >"conftest.out" 2>/dev/null || break+ diff "conftest.out" "conftest.nl" >/dev/null 2>&1 || break+ ac_count=`expr $ac_count + 1`+ if test $ac_count -gt ${ac_path_EGREP_max-0}; then+ # Best one so far, save it but keep looking for a better one+ ac_cv_path_EGREP="$ac_path_EGREP"+ ac_path_EGREP_max=$ac_count+ fi+ # 10*(2^10) chars as input seems more than enough+ test $ac_count -gt 10 && break+ done+ rm -f conftest.in conftest.tmp conftest.nl conftest.out;;+esac+++ $ac_path_EGREP_found && break 3+ done+done++done+IFS=$as_save_IFS+++fi++EGREP="$ac_cv_path_EGREP"+if test -z "$EGREP"; then+ { { echo "$as_me:$LINENO: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&5+echo "$as_me: error: no acceptable $ac_prog_name could be found in $PATH$PATH_SEPARATOR/usr/xpg4/bin" >&2;}+ { (exit 1); exit 1; }; }+fi++else+ ac_cv_path_EGREP=$EGREP+fi+++ fi+fi+{ echo "$as_me:$LINENO: result: $ac_cv_path_EGREP" >&5+echo "${ECHO_T}$ac_cv_path_EGREP" >&6; }+ EGREP="$ac_cv_path_EGREP"+++{ echo "$as_me:$LINENO: checking for ANSI C header files" >&5+echo $ECHO_N "checking for ANSI C header files... $ECHO_C" >&6; }+if test "${ac_cv_header_stdc+set}" = set; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <stdlib.h>+#include <stdarg.h>+#include <string.h>+#include <float.h>++int+main ()+{++ ;+ return 0;+}+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_cv_header_stdc=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_cv_header_stdc=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext++if test $ac_cv_header_stdc = yes; then+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <string.h>++_ACEOF+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |+ $EGREP "memchr" >/dev/null 2>&1; then+ :+else+ ac_cv_header_stdc=no+fi+rm -f -r conftest*++fi++if test $ac_cv_header_stdc = yes; then+ # ISC 2.0.2 stdlib.h does not declare free, contrary to ANSI.+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <stdlib.h>++_ACEOF+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |+ $EGREP "free" >/dev/null 2>&1; then+ :+else+ ac_cv_header_stdc=no+fi+rm -f -r conftest*++fi++if test $ac_cv_header_stdc = yes; then+ # /bin/cc in Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.+ if test "$cross_compiling" = yes; then+ :+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <ctype.h>+#include <stdlib.h>+#if ((' ' & 0x0FF) == 0x020)+# define ISLOWER(c) ('a' <= (c) && (c) <= 'z')+# define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))+#else+# define ISLOWER(c) \+ (('a' <= (c) && (c) <= 'i') \+ || ('j' <= (c) && (c) <= 'r') \+ || ('s' <= (c) && (c) <= 'z'))+# define TOUPPER(c) (ISLOWER(c) ? ((c) | 0x40) : (c))+#endif++#define XOR(e, f) (((e) && !(f)) || (!(e) && (f)))+int+main ()+{+ int i;+ for (i = 0; i < 256; i++)+ if (XOR (islower (i), ISLOWER (i))+ || toupper (i) != TOUPPER (i))+ return 2;+ return 0;+}+_ACEOF+rm -f conftest$ac_exeext+if { (ac_try="$ac_link"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_link") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && { ac_try='./conftest$ac_exeext'+ { (case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_try") 2>&5+ ac_status=$?+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); }; }; then+ :+else+ echo "$as_me: program exited with status $ac_status" >&5+echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++( exit $ac_status )+ac_cv_header_stdc=no+fi+rm -f core *.core core.conftest.* gmon.out bb.out conftest$ac_exeext conftest.$ac_objext conftest.$ac_ext+fi+++fi+fi+{ echo "$as_me:$LINENO: result: $ac_cv_header_stdc" >&5+echo "${ECHO_T}$ac_cv_header_stdc" >&6; }+if test $ac_cv_header_stdc = yes; then++cat >>confdefs.h <<\_ACEOF+#define STDC_HEADERS 1+_ACEOF++fi++# On IRIX 5.3, sys/types and inttypes.h are conflicting.++++++++++for ac_header in sys/types.h sys/stat.h stdlib.h string.h memory.h strings.h \+ inttypes.h stdint.h unistd.h+do+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`+{ echo "$as_me:$LINENO: checking for $ac_header" >&5+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default++#include <$ac_header>+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ eval "$as_ac_Header=yes"+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ eval "$as_ac_Header=no"+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+fi+ac_res=`eval echo '${'$as_ac_Header'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }+if test `eval echo '${'$as_ac_Header'}'` = yes; then+ cat >>confdefs.h <<_ACEOF+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1+_ACEOF++fi++done++++for ac_header in snappy.h+do+as_ac_Header=`echo "ac_cv_header_$ac_header" | $as_tr_sh`+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ { echo "$as_me:$LINENO: checking for $ac_header" >&5+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+fi+ac_res=`eval echo '${'$as_ac_Header'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }+else+ # Is the header compilable?+{ echo "$as_me:$LINENO: checking $ac_header usability" >&5+echo $ECHO_N "checking $ac_header usability... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+$ac_includes_default+#include <$ac_header>+_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then+ ac_header_compiler=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_header_compiler=no+fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+{ echo "$as_me:$LINENO: result: $ac_header_compiler" >&5+echo "${ECHO_T}$ac_header_compiler" >&6; }++# Is the header present?+{ echo "$as_me:$LINENO: checking $ac_header presence" >&5+echo $ECHO_N "checking $ac_header presence... $ECHO_C" >&6; }+cat >conftest.$ac_ext <<_ACEOF+/* confdefs.h. */+_ACEOF+cat confdefs.h >>conftest.$ac_ext+cat >>conftest.$ac_ext <<_ACEOF+/* end confdefs.h. */+#include <$ac_header>+_ACEOF+if { (ac_try="$ac_cpp conftest.$ac_ext"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_cpp conftest.$ac_ext") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } >/dev/null && {+ test -z "$ac_cxx_preproc_warn_flag$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ }; then+ ac_header_preproc=yes+else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5++ ac_header_preproc=no+fi++rm -f conftest.err conftest.$ac_ext+{ echo "$as_me:$LINENO: result: $ac_header_preproc" >&5+echo "${ECHO_T}$ac_header_preproc" >&6; }++# So? What about this header?+case $ac_header_compiler:$ac_header_preproc:$ac_cxx_preproc_warn_flag in+ yes:no: )+ { echo "$as_me:$LINENO: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&5+echo "$as_me: WARNING: $ac_header: accepted by the compiler, rejected by the preprocessor!" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the compiler's result" >&5+echo "$as_me: WARNING: $ac_header: proceeding with the compiler's result" >&2;}+ ac_header_preproc=yes+ ;;+ no:yes:* )+ { echo "$as_me:$LINENO: WARNING: $ac_header: present but cannot be compiled" >&5+echo "$as_me: WARNING: $ac_header: present but cannot be compiled" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: check for missing prerequisite headers?" >&5+echo "$as_me: WARNING: $ac_header: check for missing prerequisite headers?" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: see the Autoconf documentation" >&5+echo "$as_me: WARNING: $ac_header: see the Autoconf documentation" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&5+echo "$as_me: WARNING: $ac_header: section \"Present But Cannot Be Compiled\"" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: proceeding with the preprocessor's result" >&5+echo "$as_me: WARNING: $ac_header: proceeding with the preprocessor's result" >&2;}+ { echo "$as_me:$LINENO: WARNING: $ac_header: in the future, the compiler will take precedence" >&5+echo "$as_me: WARNING: $ac_header: in the future, the compiler will take precedence" >&2;}+ ( cat <<\_ASBOX+## -------------------------------- ##+## Report this to mad.one@gmail.com ##+## -------------------------------- ##+_ASBOX+ ) | sed "s/^/$as_me: WARNING: /" >&2+ ;;+esac+{ echo "$as_me:$LINENO: checking for $ac_header" >&5+echo $ECHO_N "checking for $ac_header... $ECHO_C" >&6; }+if { as_var=$as_ac_Header; eval "test \"\${$as_var+set}\" = set"; }; then+ echo $ECHO_N "(cached) $ECHO_C" >&6+else+ eval "$as_ac_Header=\$ac_header_preproc"+fi+ac_res=`eval echo '${'$as_ac_Header'}'`+ { echo "$as_me:$LINENO: result: $ac_res" >&5+echo "${ECHO_T}$ac_res" >&6; }++fi+if test `eval echo '${'$as_ac_Header'}'` = yes; then+ cat >>confdefs.h <<_ACEOF+#define `echo "HAVE_$ac_header" | $as_tr_cpp` 1+_ACEOF+ snappy=yes+else+ snappy=no+fi++done+++if test "x$snappy" = "xyes"+then+ SNAPPY="-DSNAPPY"+ SNAPPYLIB="snappy"+else+ SNAPPY=""+ SNAPPYLIB=""+fi+++{ echo "$as_me:$LINENO: checking whether compiler supports C++11 cstdatomic" >&5+echo $ECHO_N "checking whether compiler supports C++11 cstdatomic... $ECHO_C" >&6; }++OLD_CXXFLAGS="$CXXFLAGS"+CXXFLAGS="$CXXFLAGS -std=c++0x"+cat >conftest.$ac_ext <<_ACEOF++#include <cstdatomic>+int main() {}++_ACEOF+rm -f conftest.$ac_objext+if { (ac_try="$ac_compile"+case "(($ac_try" in+ *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;;+ *) ac_try_echo=$ac_try;;+esac+eval "echo \"\$as_me:$LINENO: $ac_try_echo\"") >&5+ (eval "$ac_compile") 2>conftest.er1+ ac_status=$?+ grep -v '^ *+' conftest.er1 >conftest.err+ rm -f conftest.er1+ cat conftest.err >&5+ echo "$as_me:$LINENO: \$? = $ac_status" >&5+ (exit $ac_status); } && {+ test -z "$ac_cxx_werror_flag" ||+ test ! -s conftest.err+ } && test -s conftest.$ac_objext; then++ { echo "$as_me:$LINENO: result: yes" >&5+echo "${ECHO_T}yes" >&6; }+ has_cstdatomic=yes++else+ echo "$as_me: failed program was:" >&5+sed 's/^/| /' conftest.$ac_ext >&5+++ { echo "$as_me:$LINENO: result: no" >&5+echo "${ECHO_T}no" >&6; }+ has_cstdatomic=no++fi++rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext+CXXFLAGS="$OLD_CXXFLAGS"++if test "x$has_cstdatomic" = "xyes"+then+ CSTDATOMIC="-DLEVELDB_CSTDATOMIC_PRESENT -std=c++0x"+else+ CSTDATOMIC=""+fi++++++++ac_config_files="$ac_config_files leveldb-haskell.buildinfo"++cat >confcache <<\_ACEOF+# This file is a shell script that caches the results of configure+# tests run on this system so they can be shared between configure+# scripts and configure runs, see configure's option --config-cache.+# It is not useful on other systems. If it contains results you don't+# want to keep, you may remove or edit it.+#+# config.status only pays attention to the cache file if you give it+# the --recheck option to rerun configure.+#+# `ac_cv_env_foo' variables (set or unset) will be overridden when+# loading this file, other *unset* `ac_cv_foo' will be assigned the+# following values.++_ACEOF++# The following way of writing the cache mishandles newlines in values,+# but we know of no workaround that is simple, portable, and efficient.+# So, we kill variables containing newlines.+# Ultrix sh set writes to stderr and can't be redirected directly,+# and sets the high bit in the cache file unless we assign to the vars.+(+ for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do+ eval ac_val=\$$ac_var+ case $ac_val in #(+ *${as_nl}*)+ case $ac_var in #(+ *_cv_*) { echo "$as_me:$LINENO: WARNING: Cache variable $ac_var contains a newline." >&5+echo "$as_me: WARNING: Cache variable $ac_var contains a newline." >&2;} ;;+ esac+ case $ac_var in #(+ _ | IFS | as_nl) ;; #(+ *) $as_unset $ac_var ;;+ esac ;;+ esac+ done++ (set) 2>&1 |+ case $as_nl`(ac_space=' '; set) 2>&1` in #(+ *${as_nl}ac_space=\ *)+ # `set' does not quote correctly, so add quotes (double-quote+ # substitution turns \\\\ into \\, and sed turns \\ into \).+ sed -n \+ "s/'/'\\\\''/g;+ s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p"+ ;; #(+ *)+ # `set' quotes correctly as required by POSIX, so do not add quotes.+ sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p"+ ;;+ esac |+ sort+) |+ sed '+ /^ac_cv_env_/b end+ t clear+ :clear+ s/^\([^=]*\)=\(.*[{}].*\)$/test "${\1+set}" = set || &/+ t end+ s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/+ :end' >>confcache+if diff "$cache_file" confcache >/dev/null 2>&1; then :; else+ if test -w "$cache_file"; then+ test "x$cache_file" != "x/dev/null" &&+ { echo "$as_me:$LINENO: updating cache $cache_file" >&5+echo "$as_me: updating cache $cache_file" >&6;}+ cat confcache >$cache_file+ else+ { echo "$as_me:$LINENO: not updating unwritable cache $cache_file" >&5+echo "$as_me: not updating unwritable cache $cache_file" >&6;}+ fi+fi+rm -f confcache++test "x$prefix" = xNONE && prefix=$ac_default_prefix+# Let make expand exec_prefix.+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'++# Transform confdefs.h into DEFS.+# Protect against shell expansion while executing Makefile rules.+# Protect against Makefile macro expansion.+#+# If the first sed substitution is executed (which looks for macros that+# take arguments), then branch to the quote section. Otherwise,+# look for a macro that doesn't take arguments.+ac_script='+t clear+:clear+s/^[ ]*#[ ]*define[ ][ ]*\([^ (][^ (]*([^)]*)\)[ ]*\(.*\)/-D\1=\2/g+t quote+s/^[ ]*#[ ]*define[ ][ ]*\([^ ][^ ]*\)[ ]*\(.*\)/-D\1=\2/g+t quote+b any+:quote+s/[ `~#$^&*(){}\\|;'\''"<>?]/\\&/g+s/\[/\\&/g+s/\]/\\&/g+s/\$/$$/g+H+:any+${+ g+ s/^\n//+ s/\n/ /g+ p+}+'+DEFS=`sed -n "$ac_script" confdefs.h`+++ac_libobjs=+ac_ltlibobjs=+for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue+ # 1. Remove the extension, and $U if already installed.+ ac_script='s/\$U\././;s/\.o$//;s/\.obj$//'+ ac_i=`echo "$ac_i" | sed "$ac_script"`+ # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR+ # will be set to the directory where LIBOBJS objects are built.+ ac_libobjs="$ac_libobjs \${LIBOBJDIR}$ac_i\$U.$ac_objext"+ ac_ltlibobjs="$ac_ltlibobjs \${LIBOBJDIR}$ac_i"'$U.lo'+done+LIBOBJS=$ac_libobjs++LTLIBOBJS=$ac_ltlibobjs++++: ${CONFIG_STATUS=./config.status}+ac_clean_files_save=$ac_clean_files+ac_clean_files="$ac_clean_files $CONFIG_STATUS"+{ echo "$as_me:$LINENO: creating $CONFIG_STATUS" >&5+echo "$as_me: creating $CONFIG_STATUS" >&6;}+cat >$CONFIG_STATUS <<_ACEOF+#! $SHELL+# Generated by $as_me.+# Run this file to recreate the current configuration.+# Compiler output produced by configure, useful for debugging+# configure, is in config.log if it exists.++debug=false+ac_cs_recheck=false+ac_cs_silent=false+SHELL=\${CONFIG_SHELL-$SHELL}+_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF+## --------------------- ##+## M4sh Initialization. ##+## --------------------- ##++# Be more Bourne compatible+DUALCASE=1; export DUALCASE # for MKS sh+if test -n "${ZSH_VERSION+set}" && (emulate sh) >/dev/null 2>&1; then+ emulate sh+ NULLCMD=:+ # Zsh 3.x and 4.x performs word splitting on ${1+"$@"}, which+ # is contrary to our usage. Disable this feature.+ alias -g '${1+"$@"}'='"$@"'+ setopt NO_GLOB_SUBST+else+ case `(set -o) 2>/dev/null` in+ *posix*) set -o posix ;;+esac++fi+++++# PATH needs CR+# Avoid depending upon Character Ranges.+as_cr_letters='abcdefghijklmnopqrstuvwxyz'+as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ'+as_cr_Letters=$as_cr_letters$as_cr_LETTERS+as_cr_digits='0123456789'+as_cr_alnum=$as_cr_Letters$as_cr_digits++# The user is always right.+if test "${PATH_SEPARATOR+set}" != set; then+ echo "#! /bin/sh" >conf$$.sh+ echo "exit 0" >>conf$$.sh+ chmod +x conf$$.sh+ if (PATH="/nonexistent;."; conf$$.sh) >/dev/null 2>&1; then+ PATH_SEPARATOR=';'+ else+ PATH_SEPARATOR=:+ fi+ rm -f conf$$.sh+fi++# Support unset when possible.+if ( (MAIL=60; unset MAIL) || exit) >/dev/null 2>&1; then+ as_unset=unset+else+ as_unset=false+fi+++# IFS+# We need space, tab and new line, in precisely that order. Quoting is+# there to prevent editors from complaining about space-tab.+# (If _AS_PATH_WALK were called with IFS unset, it would disable word+# splitting by setting IFS to empty value.)+as_nl='+'+IFS=" "" $as_nl"++# Find who we are. Look in the path if we contain no directory separator.+case $0 in+ *[\\/]* ) as_myself=$0 ;;+ *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR+for as_dir in $PATH+do+ IFS=$as_save_IFS+ test -z "$as_dir" && as_dir=.+ test -r "$as_dir/$0" && as_myself=$as_dir/$0 && break+done+IFS=$as_save_IFS++ ;;+esac+# We did not find ourselves, most probably we were run as `sh COMMAND'+# in which case we are not to be found in the path.+if test "x$as_myself" = x; then+ as_myself=$0+fi+if test ! -f "$as_myself"; then+ echo "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2+ { (exit 1); exit 1; }+fi++# Work around bugs in pre-3.0 UWIN ksh.+for as_var in ENV MAIL MAILPATH+do ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+done+PS1='$ '+PS2='> '+PS4='+ '++# NLS nuisances.+for as_var in \+ LANG LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_CTYPE LC_IDENTIFICATION \+ LC_MEASUREMENT LC_MESSAGES LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER \+ LC_TELEPHONE LC_TIME+do+ if (set +x; test -z "`(eval $as_var=C; export $as_var) 2>&1`"); then+ eval $as_var=C; export $as_var+ else+ ($as_unset $as_var) >/dev/null 2>&1 && $as_unset $as_var+ fi+done++# Required to use basename.+if expr a : '\(a\)' >/dev/null 2>&1 &&+ test "X`expr 00001 : '.*\(...\)'`" = X001; then+ as_expr=expr+else+ as_expr=false+fi++if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then+ as_basename=basename+else+ as_basename=false+fi+++# Name of the executable.+as_me=`$as_basename -- "$0" ||+$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \+ X"$0" : 'X\(//\)$' \| \+ X"$0" : 'X\(/\)' \| . 2>/dev/null ||+echo X/"$0" |+ sed '/^.*\/\([^/][^/]*\)\/*$/{+ s//\1/+ q+ }+ /^X\/\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\/\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`++# CDPATH.+$as_unset CDPATH++++ as_lineno_1=$LINENO+ as_lineno_2=$LINENO+ test "x$as_lineno_1" != "x$as_lineno_2" &&+ test "x`expr $as_lineno_1 + 1`" = "x$as_lineno_2" || {++ # Create $as_me.lineno as a copy of $as_myself, but with $LINENO+ # uniformly replaced by the line number. The first 'sed' inserts a+ # line-number line after each line using $LINENO; the second 'sed'+ # does the real work. The second script uses 'N' to pair each+ # line-number line with the line containing $LINENO, and appends+ # trailing '-' during substitution so that $LINENO is not a special+ # case at line end.+ # (Raja R Harinath suggested sed '=', and Paul Eggert wrote the+ # scripts with optimization help from Paolo Bonzini. Blame Lee+ # E. McMahon (1931-1989) for sed's syntax. :-)+ sed -n '+ p+ /[$]LINENO/=+ ' <$as_myself |+ sed '+ s/[$]LINENO.*/&-/+ t lineno+ b+ :lineno+ N+ :loop+ s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/+ t loop+ s/-\n.*//+ ' >$as_me.lineno &&+ chmod +x "$as_me.lineno" ||+ { echo "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2+ { (exit 1); exit 1; }; }++ # Don't try to exec as it changes $[0], causing all sort of problems+ # (the dirname of $[0] is not the place where we might find the+ # original and so on. Autoconf is especially sensitive to this).+ . "./$as_me.lineno"+ # Exit status is that of the last command.+ exit+}+++if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then+ as_dirname=dirname+else+ as_dirname=false+fi++ECHO_C= ECHO_N= ECHO_T=+case `echo -n x` in+-n*)+ case `echo 'x\c'` in+ *c*) ECHO_T=' ';; # ECHO_T is single tab character.+ *) ECHO_C='\c';;+ esac;;+*)+ ECHO_N='-n';;+esac++if expr a : '\(a\)' >/dev/null 2>&1 &&+ test "X`expr 00001 : '.*\(...\)'`" = X001; then+ as_expr=expr+else+ as_expr=false+fi++rm -f conf$$ conf$$.exe conf$$.file+if test -d conf$$.dir; then+ rm -f conf$$.dir/conf$$.file+else+ rm -f conf$$.dir+ mkdir conf$$.dir+fi+echo >conf$$.file+if ln -s conf$$.file conf$$ 2>/dev/null; then+ as_ln_s='ln -s'+ # ... but there are two gotchas:+ # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail.+ # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable.+ # In both cases, we have to default to `cp -p'.+ ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe ||+ as_ln_s='cp -p'+elif ln conf$$.file conf$$ 2>/dev/null; then+ as_ln_s=ln+else+ as_ln_s='cp -p'+fi+rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file+rmdir conf$$.dir 2>/dev/null++if mkdir -p . 2>/dev/null; then+ as_mkdir_p=:+else+ test -d ./-p && rmdir ./-p+ as_mkdir_p=false+fi++if test -x / >/dev/null 2>&1; then+ as_test_x='test -x'+else+ if ls -dL / >/dev/null 2>&1; then+ as_ls_L_option=L+ else+ as_ls_L_option=+ fi+ as_test_x='+ eval sh -c '\''+ if test -d "$1"; then+ test -d "$1/.";+ else+ case $1 in+ -*)set "./$1";;+ esac;+ case `ls -ld'$as_ls_L_option' "$1" 2>/dev/null` in+ ???[sx]*):;;*)false;;esac;fi+ '\'' sh+ '+fi+as_executable_p=$as_test_x++# Sed expression to map a string onto a valid CPP name.+as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'"++# Sed expression to map a string onto a valid variable name.+as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'"+++exec 6>&1++# Save the log message, to keep $[0] and so on meaningful, and to+# report actual input values of CONFIG_FILES etc. instead of their+# values after options handling.+ac_log="+This file was extended by Haskell LevelDB package $as_me 0.1, which was+generated by GNU Autoconf 2.61. Invocation command line was++ CONFIG_FILES = $CONFIG_FILES+ CONFIG_HEADERS = $CONFIG_HEADERS+ CONFIG_LINKS = $CONFIG_LINKS+ CONFIG_COMMANDS = $CONFIG_COMMANDS+ $ $0 $@++on `(hostname || uname -n) 2>/dev/null | sed 1q`+"++_ACEOF++cat >>$CONFIG_STATUS <<_ACEOF+# Files that config.status was made for.+config_files="$ac_config_files"++_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF+ac_cs_usage="\+\`$as_me' instantiates files from templates according to the+current configuration.++Usage: $0 [OPTIONS] [FILE]...++ -h, --help print this help, then exit+ -V, --version print version number and configuration settings, then exit+ -q, --quiet do not print progress messages+ -d, --debug don't remove temporary files+ --recheck update $as_me by reconfiguring in the same conditions+ --file=FILE[:TEMPLATE]+ instantiate the configuration file FILE++Configuration files:+$config_files++Report bugs to <bug-autoconf@gnu.org>."++_ACEOF+cat >>$CONFIG_STATUS <<_ACEOF+ac_cs_version="\\+Haskell LevelDB package config.status 0.1+configured by $0, generated by GNU Autoconf 2.61,+ with options \\"`echo "$ac_configure_args" | sed 's/^ //; s/[\\""\`\$]/\\\\&/g'`\\"++Copyright (C) 2006 Free Software Foundation, Inc.+This config.status script is free software; the Free Software Foundation+gives unlimited permission to copy, distribute and modify it."++ac_pwd='$ac_pwd'+srcdir='$srcdir'+_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF+# If no file are specified by the user, then we need to provide default+# value. By we need to know if files were specified by the user.+ac_need_defaults=:+while test $# != 0+do+ case $1 in+ --*=*)+ ac_option=`expr "X$1" : 'X\([^=]*\)='`+ ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'`+ ac_shift=:+ ;;+ *)+ ac_option=$1+ ac_optarg=$2+ ac_shift=shift+ ;;+ esac++ case $ac_option in+ # Handling of the options.+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)+ ac_cs_recheck=: ;;+ --version | --versio | --versi | --vers | --ver | --ve | --v | -V )+ echo "$ac_cs_version"; exit ;;+ --debug | --debu | --deb | --de | --d | -d )+ debug=: ;;+ --file | --fil | --fi | --f )+ $ac_shift+ CONFIG_FILES="$CONFIG_FILES $ac_optarg"+ ac_need_defaults=false;;+ --he | --h | --help | --hel | -h )+ echo "$ac_cs_usage"; exit ;;+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \+ | -silent | --silent | --silen | --sile | --sil | --si | --s)+ ac_cs_silent=: ;;++ # This is an error.+ -*) { echo "$as_me: error: unrecognized option: $1+Try \`$0 --help' for more information." >&2+ { (exit 1); exit 1; }; } ;;++ *) ac_config_targets="$ac_config_targets $1"+ ac_need_defaults=false ;;++ esac+ shift+done++ac_configure_extra_args=++if $ac_cs_silent; then+ exec 6>/dev/null+ ac_configure_extra_args="$ac_configure_extra_args --silent"+fi++_ACEOF+cat >>$CONFIG_STATUS <<_ACEOF+if \$ac_cs_recheck; then+ echo "running CONFIG_SHELL=$SHELL $SHELL $0 "$ac_configure_args \$ac_configure_extra_args " --no-create --no-recursion" >&6+ CONFIG_SHELL=$SHELL+ export CONFIG_SHELL+ exec $SHELL "$0"$ac_configure_args \$ac_configure_extra_args --no-create --no-recursion+fi++_ACEOF+cat >>$CONFIG_STATUS <<\_ACEOF+exec 5>>config.log+{+ echo+ sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX+## Running $as_me. ##+_ASBOX+ echo "$ac_log"+} >&5++_ACEOF+cat >>$CONFIG_STATUS <<_ACEOF+_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF++# Handling of arguments.+for ac_config_target in $ac_config_targets+do+ case $ac_config_target in+ "leveldb-haskell.buildinfo") CONFIG_FILES="$CONFIG_FILES leveldb-haskell.buildinfo" ;;++ *) { { echo "$as_me:$LINENO: error: invalid argument: $ac_config_target" >&5+echo "$as_me: error: invalid argument: $ac_config_target" >&2;}+ { (exit 1); exit 1; }; };;+ esac+done+++# If the user did not use the arguments to specify the items to instantiate,+# then the envvar interface is used. Set only those that are not.+# We use the long form for the default assignment because of an extremely+# bizarre bug on SunOS 4.1.3.+if $ac_need_defaults; then+ test "${CONFIG_FILES+set}" = set || CONFIG_FILES=$config_files+fi++# Have a temporary directory for convenience. Make it in the build tree+# simply because there is no reason against having it here, and in addition,+# creating and moving files from /tmp can sometimes cause problems.+# Hook for its removal unless debugging.+# Note that there is a small window in which the directory will not be cleaned:+# after its creation but before its name has been assigned to `$tmp'.+$debug ||+{+ tmp=+ trap 'exit_status=$?+ { test -z "$tmp" || test ! -d "$tmp" || rm -fr "$tmp"; } && exit $exit_status+' 0+ trap '{ (exit 1); exit 1; }' 1 2 13 15+}+# Create a (secure) tmp directory for tmp files.++{+ tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` &&+ test -n "$tmp" && test -d "$tmp"+} ||+{+ tmp=./conf$$-$RANDOM+ (umask 077 && mkdir "$tmp")+} ||+{+ echo "$me: cannot create a temporary directory in ." >&2+ { (exit 1); exit 1; }+}++#+# Set up the sed scripts for CONFIG_FILES section.+#++# No need to generate the scripts if there are no CONFIG_FILES.+# This happens for instance when ./config.status config.h+if test -n "$CONFIG_FILES"; then++_ACEOF++++ac_delim='%!_!# '+for ac_last_try in false false false false false :; do+ cat >conf$$subs.sed <<_ACEOF+SHELL!$SHELL$ac_delim+PATH_SEPARATOR!$PATH_SEPARATOR$ac_delim+PACKAGE_NAME!$PACKAGE_NAME$ac_delim+PACKAGE_TARNAME!$PACKAGE_TARNAME$ac_delim+PACKAGE_VERSION!$PACKAGE_VERSION$ac_delim+PACKAGE_STRING!$PACKAGE_STRING$ac_delim+PACKAGE_BUGREPORT!$PACKAGE_BUGREPORT$ac_delim+exec_prefix!$exec_prefix$ac_delim+prefix!$prefix$ac_delim+program_transform_name!$program_transform_name$ac_delim+bindir!$bindir$ac_delim+sbindir!$sbindir$ac_delim+libexecdir!$libexecdir$ac_delim+datarootdir!$datarootdir$ac_delim+datadir!$datadir$ac_delim+sysconfdir!$sysconfdir$ac_delim+sharedstatedir!$sharedstatedir$ac_delim+localstatedir!$localstatedir$ac_delim+includedir!$includedir$ac_delim+oldincludedir!$oldincludedir$ac_delim+docdir!$docdir$ac_delim+infodir!$infodir$ac_delim+htmldir!$htmldir$ac_delim+dvidir!$dvidir$ac_delim+pdfdir!$pdfdir$ac_delim+psdir!$psdir$ac_delim+libdir!$libdir$ac_delim+localedir!$localedir$ac_delim+mandir!$mandir$ac_delim+DEFS!$DEFS$ac_delim+ECHO_C!$ECHO_C$ac_delim+ECHO_N!$ECHO_N$ac_delim+ECHO_T!$ECHO_T$ac_delim+LIBS!$LIBS$ac_delim+build_alias!$build_alias$ac_delim+host_alias!$host_alias$ac_delim+target_alias!$target_alias$ac_delim+CXX!$CXX$ac_delim+CXXFLAGS!$CXXFLAGS$ac_delim+LDFLAGS!$LDFLAGS$ac_delim+CPPFLAGS!$CPPFLAGS$ac_delim+ac_ct_CXX!$ac_ct_CXX$ac_delim+EXEEXT!$EXEEXT$ac_delim+OBJEXT!$OBJEXT$ac_delim+CXXCPP!$CXXCPP$ac_delim+GREP!$GREP$ac_delim+EGREP!$EGREP$ac_delim+SNAPPY!$SNAPPY$ac_delim+SNAPPYLIB!$SNAPPYLIB$ac_delim+CSTDATOMIC!$CSTDATOMIC$ac_delim+LIBOBJS!$LIBOBJS$ac_delim+LTLIBOBJS!$LTLIBOBJS$ac_delim+_ACEOF++ if test `sed -n "s/.*$ac_delim\$/X/p" conf$$subs.sed | grep -c X` = 52; then+ break+ elif $ac_last_try; then+ { { echo "$as_me:$LINENO: error: could not make $CONFIG_STATUS" >&5+echo "$as_me: error: could not make $CONFIG_STATUS" >&2;}+ { (exit 1); exit 1; }; }+ else+ ac_delim="$ac_delim!$ac_delim _$ac_delim!! "+ fi+done++ac_eof=`sed -n '/^CEOF[0-9]*$/s/CEOF/0/p' conf$$subs.sed`+if test -n "$ac_eof"; then+ ac_eof=`echo "$ac_eof" | sort -nru | sed 1q`+ ac_eof=`expr $ac_eof + 1`+fi++cat >>$CONFIG_STATUS <<_ACEOF+cat >"\$tmp/subs-1.sed" <<\CEOF$ac_eof+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b end+_ACEOF+sed '+s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g+s/^/s,@/; s/!/@,|#_!!_#|/+:n+t n+s/'"$ac_delim"'$/,g/; t+s/$/\\/; p+N; s/^.*\n//; s/[,\\&]/\\&/g; s/@/@|#_!!_#|/g; b n+' >>$CONFIG_STATUS <conf$$subs.sed+rm -f conf$$subs.sed+cat >>$CONFIG_STATUS <<_ACEOF+:end+s/|#_!!_#|//g+CEOF$ac_eof+_ACEOF+++# VPATH may cause trouble with some makes, so we remove $(srcdir),+# ${srcdir} and @srcdir@ from VPATH if srcdir is ".", strip leading and+# trailing colons and then remove the whole line if VPATH becomes empty+# (actually we leave an empty line to preserve line numbers).+if test "x$srcdir" = x.; then+ ac_vpsub='/^[ ]*VPATH[ ]*=/{+s/:*\$(srcdir):*/:/+s/:*\${srcdir}:*/:/+s/:*@srcdir@:*/:/+s/^\([^=]*=[ ]*\):*/\1/+s/:*$//+s/^[^=]*=[ ]*$//+}'+fi++cat >>$CONFIG_STATUS <<\_ACEOF+fi # test -n "$CONFIG_FILES"+++for ac_tag in :F $CONFIG_FILES+do+ case $ac_tag in+ :[FHLC]) ac_mode=$ac_tag; continue;;+ esac+ case $ac_mode$ac_tag in+ :[FHL]*:*);;+ :L* | :C*:*) { { echo "$as_me:$LINENO: error: Invalid tag $ac_tag." >&5+echo "$as_me: error: Invalid tag $ac_tag." >&2;}+ { (exit 1); exit 1; }; };;+ :[FH]-) ac_tag=-:-;;+ :[FH]*) ac_tag=$ac_tag:$ac_tag.in;;+ esac+ ac_save_IFS=$IFS+ IFS=:+ set x $ac_tag+ IFS=$ac_save_IFS+ shift+ ac_file=$1+ shift++ case $ac_mode in+ :L) ac_source=$1;;+ :[FH])+ ac_file_inputs=+ for ac_f+ do+ case $ac_f in+ -) ac_f="$tmp/stdin";;+ *) # Look for the file first in the build tree, then in the source tree+ # (if the path is not absolute). The absolute path cannot be DOS-style,+ # because $ac_f cannot contain `:'.+ test -f "$ac_f" ||+ case $ac_f in+ [\\/$]*) false;;+ *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";;+ esac ||+ { { echo "$as_me:$LINENO: error: cannot find input file: $ac_f" >&5+echo "$as_me: error: cannot find input file: $ac_f" >&2;}+ { (exit 1); exit 1; }; };;+ esac+ ac_file_inputs="$ac_file_inputs $ac_f"+ done++ # Let's still pretend it is `configure' which instantiates (i.e., don't+ # use $as_me), people would be surprised to read:+ # /* config.h. Generated by config.status. */+ configure_input="Generated from "`IFS=:+ echo $* | sed 's|^[^:]*/||;s|:[^:]*/|, |g'`" by configure."+ if test x"$ac_file" != x-; then+ configure_input="$ac_file. $configure_input"+ { echo "$as_me:$LINENO: creating $ac_file" >&5+echo "$as_me: creating $ac_file" >&6;}+ fi++ case $ac_tag in+ *:-:* | *:-) cat >"$tmp/stdin";;+ esac+ ;;+ esac++ ac_dir=`$as_dirname -- "$ac_file" ||+$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \+ X"$ac_file" : 'X\(//\)[^/]' \| \+ X"$ac_file" : 'X\(//\)$' \| \+ X"$ac_file" : 'X\(/\)' \| . 2>/dev/null ||+echo X"$ac_file" |+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{+ s//\1/+ q+ }+ /^X\(\/\/\)[^/].*/{+ s//\1/+ q+ }+ /^X\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`+ { as_dir="$ac_dir"+ case $as_dir in #(+ -*) as_dir=./$as_dir;;+ esac+ test -d "$as_dir" || { $as_mkdir_p && mkdir -p "$as_dir"; } || {+ as_dirs=+ while :; do+ case $as_dir in #(+ *\'*) as_qdir=`echo "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #(+ *) as_qdir=$as_dir;;+ esac+ as_dirs="'$as_qdir' $as_dirs"+ as_dir=`$as_dirname -- "$as_dir" ||+$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \+ X"$as_dir" : 'X\(//\)[^/]' \| \+ X"$as_dir" : 'X\(//\)$' \| \+ X"$as_dir" : 'X\(/\)' \| . 2>/dev/null ||+echo X"$as_dir" |+ sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{+ s//\1/+ q+ }+ /^X\(\/\/\)[^/].*/{+ s//\1/+ q+ }+ /^X\(\/\/\)$/{+ s//\1/+ q+ }+ /^X\(\/\).*/{+ s//\1/+ q+ }+ s/.*/./; q'`+ test -d "$as_dir" && break+ done+ test -z "$as_dirs" || eval "mkdir $as_dirs"+ } || test -d "$as_dir" || { { echo "$as_me:$LINENO: error: cannot create directory $as_dir" >&5+echo "$as_me: error: cannot create directory $as_dir" >&2;}+ { (exit 1); exit 1; }; }; }+ ac_builddir=.++case "$ac_dir" in+.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;;+*)+ ac_dir_suffix=/`echo "$ac_dir" | sed 's,^\.[\\/],,'`+ # A ".." for each directory in $ac_dir_suffix.+ ac_top_builddir_sub=`echo "$ac_dir_suffix" | sed 's,/[^\\/]*,/..,g;s,/,,'`+ case $ac_top_builddir_sub in+ "") ac_top_builddir_sub=. ac_top_build_prefix= ;;+ *) ac_top_build_prefix=$ac_top_builddir_sub/ ;;+ esac ;;+esac+ac_abs_top_builddir=$ac_pwd+ac_abs_builddir=$ac_pwd$ac_dir_suffix+# for backward compatibility:+ac_top_builddir=$ac_top_build_prefix++case $srcdir in+ .) # We are building in place.+ ac_srcdir=.+ ac_top_srcdir=$ac_top_builddir_sub+ ac_abs_top_srcdir=$ac_pwd ;;+ [\\/]* | ?:[\\/]* ) # Absolute name.+ ac_srcdir=$srcdir$ac_dir_suffix;+ ac_top_srcdir=$srcdir+ ac_abs_top_srcdir=$srcdir ;;+ *) # Relative name.+ ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix+ ac_top_srcdir=$ac_top_build_prefix$srcdir+ ac_abs_top_srcdir=$ac_pwd/$srcdir ;;+esac+ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix+++ case $ac_mode in+ :F)+ #+ # CONFIG_FILE+ #++_ACEOF++cat >>$CONFIG_STATUS <<\_ACEOF+# If the template does not know about datarootdir, expand it.+# FIXME: This hack should be removed a few years after 2.60.+ac_datarootdir_hack=; ac_datarootdir_seen=++case `sed -n '/datarootdir/ {+ p+ q+}+/@datadir@/p+/@docdir@/p+/@infodir@/p+/@localedir@/p+/@mandir@/p+' $ac_file_inputs` in+*datarootdir*) ac_datarootdir_seen=yes;;+*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*)+ { echo "$as_me:$LINENO: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5+echo "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;}+_ACEOF+cat >>$CONFIG_STATUS <<_ACEOF+ ac_datarootdir_hack='+ s&@datadir@&$datadir&g+ s&@docdir@&$docdir&g+ s&@infodir@&$infodir&g+ s&@localedir@&$localedir&g+ s&@mandir@&$mandir&g+ s&\\\${datarootdir}&$datarootdir&g' ;;+esac+_ACEOF++# Neutralize VPATH when `$srcdir' = `.'.+# Shell code in configure.ac might set extrasub.+# FIXME: do we really want to maintain this feature?+cat >>$CONFIG_STATUS <<_ACEOF+ sed "$ac_vpsub+$extrasub+_ACEOF+cat >>$CONFIG_STATUS <<\_ACEOF+:t+/@[a-zA-Z_][a-zA-Z_0-9]*@/!b+s&@configure_input@&$configure_input&;t t+s&@top_builddir@&$ac_top_builddir_sub&;t t+s&@srcdir@&$ac_srcdir&;t t+s&@abs_srcdir@&$ac_abs_srcdir&;t t+s&@top_srcdir@&$ac_top_srcdir&;t t+s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t+s&@builddir@&$ac_builddir&;t t+s&@abs_builddir@&$ac_abs_builddir&;t t+s&@abs_top_builddir@&$ac_abs_top_builddir&;t t+$ac_datarootdir_hack+" $ac_file_inputs | sed -f "$tmp/subs-1.sed" >$tmp/out++test -z "$ac_datarootdir_hack$ac_datarootdir_seen" &&+ { ac_out=`sed -n '/\${datarootdir}/p' "$tmp/out"`; test -n "$ac_out"; } &&+ { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' "$tmp/out"`; test -z "$ac_out"; } &&+ { echo "$as_me:$LINENO: WARNING: $ac_file contains a reference to the variable \`datarootdir'+which seems to be undefined. Please make sure it is defined." >&5+echo "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir'+which seems to be undefined. Please make sure it is defined." >&2;}++ rm -f "$tmp/stdin"+ case $ac_file in+ -) cat "$tmp/out"; rm -f "$tmp/out";;+ *) rm -f "$ac_file"; mv "$tmp/out" $ac_file;;+ esac+ ;;++++ esac++done # for ac_tag+++{ (exit 0); exit 0; }+_ACEOF+chmod +x $CONFIG_STATUS+ac_clean_files=$ac_clean_files_save+++# configure is writing to config.log, and then calls config.status.+# config.status does its own redirection, appending to config.log.+# Unfortunately, on DOS this fails, as config.log is still kept open+# by configure, so config.status won't be able to write to it; its+# output is simply discarded. So we exec the FD to /dev/null,+# effectively closing config.log, so it can be properly (re)opened and+# appended to by config.status. When coming back to configure, we+# need to make the FD available again.+if test "$no_create" != yes; then+ ac_cs_success=:+ ac_config_status_args=+ test "$silent" = yes &&+ ac_config_status_args="$ac_config_status_args --quiet"+ exec 5>/dev/null+ $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false+ exec 5>>config.log+ # Use ||, not &&, to avoid exiting from the if with $? = 1, which+ # would make configure fail if this is the last instruction.+ $ac_cs_success || { (exit 1); exit 1; }+fi+
+ configure.ac view
@@ -0,0 +1,64 @@+AC_INIT([Haskell LevelDB package],+ [0.1],+ [mad.one@gmail.com],+ [leveldb],+ [http://thoughtpolice.github.com/hs-leveldb])++AC_LANG_CPLUSPLUS++dnl ****************+dnl Check for snappy+dnl ****************++AC_CHECK_HEADERS([snappy.h],+ [snappy=yes],+ [snappy=no])++if test "x$snappy" = "xyes"+then+ SNAPPY="-DSNAPPY"+ SNAPPYLIB="snappy"+else+ SNAPPY=""+ SNAPPYLIB=""+fi++dnl ********************+dnl Check for cstdatomic+dnl ********************++AC_MSG_CHECKING(whether compiler supports C++11 cstdatomic)++OLD_CXXFLAGS="$CXXFLAGS"+CXXFLAGS="$CXXFLAGS -std=c++0x"+AC_COMPILE_IFELSE([+#include <cstdatomic>+int main() {}+], [+ AC_MSG_RESULT(yes)+ has_cstdatomic=yes+ ],+ [+ AC_MSG_RESULT(no)+ has_cstdatomic=no+ ])+CXXFLAGS="$OLD_CXXFLAGS"++if test "x$has_cstdatomic" = "xyes"+then+ CSTDATOMIC="-DLEVELDB_CSTDATOMIC_PRESENT -std=c++0x"+else+ CSTDATOMIC=""+fi++dnl *****************+dnl Substitute output+dnl *****************++AC_SUBST([SNAPPY])+AC_SUBST([SNAPPYLIB])++AC_SUBST([CSTDATOMIC])++AC_CONFIG_FILES([leveldb-haskell.buildinfo])+AC_OUTPUT
+ examples/features.hs view
@@ -0,0 +1,56 @@+{-# LANGUAGE OverloadedStrings #-}+-- |+-- Comprehensive walkthough of the functionality provided by this library.+--+module Main where++import Control.Monad+import Data.ByteString.Char8 hiding (take)+import Prelude hiding (putStrLn)+import System.FilePath++import Database.LevelDB+++main :: IO ()+main = withLevelDB dbdir [ CreateIfMissing, CacheSize 2048 ] $ \db -> do+ put db [] "foo" "bar"+ get db [ FillCache ] "foo" >>= print+ delete db [] "foo"+ get db [ FillCache ] "foo" >>= print++ withSnapshot db $ \snap -> do+ write db [ Sync ] [ Put "a" "one"+ , Put "b" "two"+ , Put "c" "three" ]+ dumpEntries db [ UseSnapshot snap, FillCache ]+ dumpEntries db [ FillCache ]++ approximateSize db ("a", "z") >>= print+ getProperty db SSTables >>= printProperty "sstables"+ getProperty db Stats >>= printProperty "stats"+ getProperty db (NumFilesAtLevel 1) >>= printProperty "num files at level"++ write db [ Sync ] [ Del "a", Del "b", Del "c" ]+ dumpEntries db [ FillCache ]++ where+ dbdir = "/" </> "tmp" </> "leveltest"++ dumpEntries db opts =+ withIterator db opts $ \iter -> do+ iterFirst iter+ iterEntries iter print++ iterEntries iter f = do+ valid <- iterValid iter+ when valid $ do+ key <- iterKey iter+ val <- iterValue iter+ _ <- f (key, val)+ _ <- iterNext iter+ iterEntries iter f++ printProperty l p = do+ putStrLn l+ maybe (putStrLn "n/a") putStrLn $ p
+ leveldb-haskell.buildinfo.in view
@@ -0,0 +1,2 @@+cc-options: @SNAPPY@ @CSTDATOMIC@+extra-libraries: @SNAPPYLIB@
leveldb-haskell.cabal view
@@ -1,15 +1,17 @@ name: leveldb-haskell-version: 0.0.1+version: 0.0.2 synopsis: Haskell bindings to LevelDB homepage: http://github.com/kim/leveldb-haskell+bug-reports: http://github.com/kim/leveldb-haskell/issues license: BSD3 license-file: LICENSE author: Kim Altintop maintainer: kim.altintop@gmail.com-category: Database+copyright: Copyright (c) 2012 The leveldb-haskell Authors+category: Database, FFI stability: Experimental build-type: Configure-cabal-version: >=1.8+cabal-version: >=1.10 tested-with: GHC == 7.2.2, GHC == 7.4.1 description: From <http://leveldb.googlecode.com>:@@ -22,13 +24,26 @@ early stage and has seen very limited testing. . Note: the LevelDB source code is bundled with this package and built as- part of the installation, assuming that a working C++ compiler is installed- on the target system and the @make@ command can invoke it.+ part of the installation. To enable compression, the snappy library needs to+ be installed on the target system. -extra-source-files: Example.lhs+extra-source-files: AUTHORS , Readme.md , configure+ , configure.ac+ , leveldb-haskell.buildinfo.in , cbits/leveldb/include/leveldb/*.h+ , cbits/leveldb/db/*.h+ , cbits/leveldb/db/*.cc+ , cbits/leveldb/table/*.h+ , cbits/leveldb/table/*.cc+ , cbits/leveldb/port/*.h+ , cbits/leveldb/port/*.cc+ , cbits/leveldb/util/*.h+ , cbits/leveldb/util/*.cc+ , cbits/leveldb/helpers/memenv/*.h+ , cbits/leveldb/helpers/memenv/*.cc+ , examples/*.hs source-repository head type: git@@ -37,21 +52,71 @@ library exposed-modules: Database.LevelDB other-modules: Database.LevelDB.Base- extensions: CPP++ default-language: Haskell2010+ default-extensions:+ CPP , ForeignFunctionInterface+ , EmptyDataDecls+ build-depends: base >= 3 && < 5 , bytestring- ghc-options: -Wall -O2 -static -optl-static -rtsopts+ , filepath++ ghc-options: -Wall -O2+ -static -optl-static -rtsopts+ -funbox-strict-fields ghc-prof-options: -prof -auto-all+ hs-source-dirs: src- includes: leveldb/c.h- extra-libraries: leveldb, snappy, stdc++- include-dirs: cbits/leveldb/include+ extra-libraries: stdc+++ include-dirs: cbits/leveldb+ cbits/leveldb/include -executable hsleveldb-example- main-is: Example.lhs- build-depends: base- , leveldb-haskell- , bytestring- , filepath- ghc-options: -rtsopts -threaded+ cc-options: -fno-builtin-memcmp -DLEVELDB_PLATFORM_POSIX++ if os(linux)+ cc-options: -DOS_LINUX+ if os(darwin)+ cc-options: -DOS_MACOSX+ if os(solaris)+ cc-options: -D_REENTRANT -DOS_SOLARIS+ if os(freebsd)+ cc-options: -D_REENTRANT -DOS_FREEBSD++ c-sources: cbits/leveldb/db/builder.cc+ cbits/leveldb/db/c.cc+ cbits/leveldb/db/db_impl.cc+ cbits/leveldb/db/db_iter.cc+ cbits/leveldb/db/dbformat.cc+ cbits/leveldb/db/filename.cc+ cbits/leveldb/db/log_reader.cc+ cbits/leveldb/db/log_writer.cc+ cbits/leveldb/db/memtable.cc+ cbits/leveldb/db/repair.cc+ cbits/leveldb/db/table_cache.cc+ cbits/leveldb/db/version_edit.cc+ cbits/leveldb/db/version_set.cc+ cbits/leveldb/db/write_batch.cc+ cbits/leveldb/helpers/memenv/memenv.cc+ cbits/leveldb/port/port_posix.cc+ cbits/leveldb/table/block.cc+ cbits/leveldb/table/block_builder.cc+ cbits/leveldb/table/format.cc+ cbits/leveldb/table/iterator.cc+ cbits/leveldb/table/merger.cc+ cbits/leveldb/table/table.cc+ cbits/leveldb/table/table_builder.cc+ cbits/leveldb/table/two_level_iterator.cc+ cbits/leveldb/util/arena.cc+ cbits/leveldb/util/cache.cc+ cbits/leveldb/util/coding.cc+ cbits/leveldb/util/comparator.cc+ cbits/leveldb/util/crc32c.cc+ cbits/leveldb/util/env.cc+ cbits/leveldb/util/env_posix.cc+ cbits/leveldb/util/hash.cc+ cbits/leveldb/util/histogram.cc+ cbits/leveldb/util/logging.cc+ cbits/leveldb/util/options.cc+ cbits/leveldb/util/status.cc
src/Database/LevelDB.hs view
@@ -1,4 +1,11 @@ -- |+-- Module : Database.LevelDB+-- Copyright : (c) 2012 Kim Altintop+-- License : BSD3+-- Maintainer : kim.altintop@gmail.com+-- Stability : experimental+-- Portability : non-portable+-- -- LevelDB Haskell binding. -- -- The API closely follows the C-API of LevelDB.
src/Database/LevelDB/Base.hsc view
@@ -1,4 +1,12 @@ {-# LANGUAGE CPP, ForeignFunctionInterface, EmptyDataDecls #-}+-- |+-- Module : Database.LevelDB.Base+-- Copyright : (c) 2012 Kim Altintop+-- License : BSD3+-- Maintainer : kim.altintop@gmail.com+-- Stability : experimental+-- Portability : non-portable+-- module Database.LevelDB.Base where