1、优化cmake进行分类
2、增加sqlserver支持
This commit is contained in:
3
3rdparty/cocos2dx/CMakeLists.txt
vendored
3
3rdparty/cocos2dx/CMakeLists.txt
vendored
@@ -14,3 +14,6 @@ add_library(${LIBRARY_NAME} ${SOURCE_FILES} ${INCLUDE_FILES})
|
||||
target_include_directories(${LIBRARY_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/include")
|
||||
target_include_directories(${LIBRARY_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/include/cocos")
|
||||
|
||||
|
||||
# 设置 Visual Studio 中的文件夹
|
||||
set_property(TARGET ${LIBRARY_NAME} PROPERTY FOLDER "3rdparty")
|
||||
2
3rdparty/ffmpeg/CMakeLists.txt
vendored
2
3rdparty/ffmpeg/CMakeLists.txt
vendored
@@ -12,3 +12,5 @@ add_library(${LIBRARY_NAME} ${SOURCE_FILES})
|
||||
# 将包含目录与目标相关联,这样只有在编译此库时才会包含这些目录
|
||||
target_include_directories(${LIBRARY_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/include")
|
||||
|
||||
|
||||
set_property(TARGET ${LIBRARY_NAME} PROPERTY FOLDER "3rdparty")
|
||||
28
3rdparty/leveldb/CMakeLists.txt
vendored
Normal file
28
3rdparty/leveldb/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
cmake_minimum_required(VERSION 3.5)
|
||||
|
||||
# 获取上级目录名做为库名
|
||||
get_filename_component(CURRENT_DIR ${CMAKE_CURRENT_SOURCE_DIR} ABSOLUTE)
|
||||
get_filename_component(LIBRARY_NAME ${CURRENT_DIR} NAME)
|
||||
|
||||
file(GLOB SOURCE_FILES "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/src/*.*")
|
||||
|
||||
|
||||
if(MSVC)
|
||||
add_definitions(
|
||||
-D_WINDOWS
|
||||
-DLEVELDB_COMPILE_LIBRARY
|
||||
-DLEVELDB_PLATFORM_WINDOWS=1
|
||||
-D_UNICODE
|
||||
-DUNICODE
|
||||
-D_HAS_EXCEPTIONS=0
|
||||
)
|
||||
endif()
|
||||
|
||||
# 创建库
|
||||
add_library(${LIBRARY_NAME} ${SOURCE_FILES})
|
||||
|
||||
# 将包含目录与目标相关联,这样只有在编译此库时才会包含这些目录
|
||||
target_include_directories(${LIBRARY_NAME} PUBLIC "${PROJECT_SOURCE_DIR}/3rdparty/${LIBRARY_NAME}/include")
|
||||
|
||||
|
||||
set_property(TARGET ${LIBRARY_NAME} PROPERTY FOLDER "3rdparty")
|
||||
270
3rdparty/leveldb/c.h
vendored
Normal file
270
3rdparty/leveldb/c.h
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
/* Copyright (c) 2011 The LevelDB Authors. All rights reserved.
|
||||
Use of this source code is governed by a BSD-style license that can be
|
||||
found in the LICENSE file. See the AUTHORS file for names of contributors.
|
||||
|
||||
C bindings for leveldb. May be useful as a stable ABI that can be
|
||||
used by programs that keep leveldb in a shared library, or for
|
||||
a JNI api.
|
||||
|
||||
Does not support:
|
||||
. getters for the option types
|
||||
. custom comparators that implement key shortening
|
||||
. custom iter, db, env, cache implementations using just the C bindings
|
||||
|
||||
Some conventions:
|
||||
|
||||
(1) We expose just opaque struct pointers and functions to clients.
|
||||
This allows us to change internal representations without having to
|
||||
recompile clients.
|
||||
|
||||
(2) For simplicity, there is no equivalent to the Slice type. Instead,
|
||||
the caller has to pass the pointer and length as separate
|
||||
arguments.
|
||||
|
||||
(3) Errors are represented by a null-terminated c string. NULL
|
||||
means no error. All operations that can raise an error are passed
|
||||
a "char** errptr" as the last argument. One of the following must
|
||||
be true on entry:
|
||||
*errptr == NULL
|
||||
*errptr points to a malloc()ed null-terminated error message
|
||||
(On Windows, *errptr must have been malloc()-ed by this library.)
|
||||
On success, a leveldb routine leaves *errptr unchanged.
|
||||
On failure, leveldb frees the old value of *errptr and
|
||||
set *errptr to a malloc()ed error message.
|
||||
|
||||
(4) Bools have the type uint8_t (0 == false; rest == true)
|
||||
|
||||
(5) All of the pointer arguments must be non-NULL.
|
||||
*/
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_C_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_C_H_
|
||||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* Exported types */
|
||||
|
||||
typedef struct leveldb_t leveldb_t;
|
||||
typedef struct leveldb_cache_t leveldb_cache_t;
|
||||
typedef struct leveldb_comparator_t leveldb_comparator_t;
|
||||
typedef struct leveldb_env_t leveldb_env_t;
|
||||
typedef struct leveldb_filelock_t leveldb_filelock_t;
|
||||
typedef struct leveldb_filterpolicy_t leveldb_filterpolicy_t;
|
||||
typedef struct leveldb_iterator_t leveldb_iterator_t;
|
||||
typedef struct leveldb_logger_t leveldb_logger_t;
|
||||
typedef struct leveldb_options_t leveldb_options_t;
|
||||
typedef struct leveldb_randomfile_t leveldb_randomfile_t;
|
||||
typedef struct leveldb_readoptions_t leveldb_readoptions_t;
|
||||
typedef struct leveldb_seqfile_t leveldb_seqfile_t;
|
||||
typedef struct leveldb_snapshot_t leveldb_snapshot_t;
|
||||
typedef struct leveldb_writablefile_t leveldb_writablefile_t;
|
||||
typedef struct leveldb_writebatch_t leveldb_writebatch_t;
|
||||
typedef struct leveldb_writeoptions_t leveldb_writeoptions_t;
|
||||
|
||||
/* DB operations */
|
||||
|
||||
LEVELDB_EXPORT leveldb_t* leveldb_open(const leveldb_options_t* options,
|
||||
const char* name, char** errptr);
|
||||
|
||||
LEVELDB_EXPORT void leveldb_close(leveldb_t* db);
|
||||
|
||||
LEVELDB_EXPORT 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);
|
||||
|
||||
LEVELDB_EXPORT void leveldb_delete(leveldb_t* db,
|
||||
const leveldb_writeoptions_t* options,
|
||||
const char* key, size_t keylen,
|
||||
char** errptr);
|
||||
|
||||
LEVELDB_EXPORT void leveldb_write(leveldb_t* db,
|
||||
const leveldb_writeoptions_t* options,
|
||||
leveldb_writebatch_t* batch, char** errptr);
|
||||
|
||||
/* Returns NULL if not found. A malloc()ed array otherwise.
|
||||
Stores the length of the array in *vallen. */
|
||||
LEVELDB_EXPORT char* leveldb_get(leveldb_t* db,
|
||||
const leveldb_readoptions_t* options,
|
||||
const char* key, size_t keylen, size_t* vallen,
|
||||
char** errptr);
|
||||
|
||||
LEVELDB_EXPORT leveldb_iterator_t* leveldb_create_iterator(
|
||||
leveldb_t* db, const leveldb_readoptions_t* options);
|
||||
|
||||
LEVELDB_EXPORT const leveldb_snapshot_t* leveldb_create_snapshot(leveldb_t* db);
|
||||
|
||||
LEVELDB_EXPORT void leveldb_release_snapshot(
|
||||
leveldb_t* db, const leveldb_snapshot_t* snapshot);
|
||||
|
||||
/* Returns NULL if property name is unknown.
|
||||
Else returns a pointer to a malloc()-ed null-terminated value. */
|
||||
LEVELDB_EXPORT char* leveldb_property_value(leveldb_t* db,
|
||||
const char* propname);
|
||||
|
||||
LEVELDB_EXPORT 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);
|
||||
|
||||
LEVELDB_EXPORT void leveldb_compact_range(leveldb_t* db, const char* start_key,
|
||||
size_t start_key_len,
|
||||
const char* limit_key,
|
||||
size_t limit_key_len);
|
||||
|
||||
/* Management operations */
|
||||
|
||||
LEVELDB_EXPORT void leveldb_destroy_db(const leveldb_options_t* options,
|
||||
const char* name, char** errptr);
|
||||
|
||||
LEVELDB_EXPORT void leveldb_repair_db(const leveldb_options_t* options,
|
||||
const char* name, char** errptr);
|
||||
|
||||
/* Iterator */
|
||||
|
||||
LEVELDB_EXPORT void leveldb_iter_destroy(leveldb_iterator_t*);
|
||||
LEVELDB_EXPORT uint8_t leveldb_iter_valid(const leveldb_iterator_t*);
|
||||
LEVELDB_EXPORT void leveldb_iter_seek_to_first(leveldb_iterator_t*);
|
||||
LEVELDB_EXPORT void leveldb_iter_seek_to_last(leveldb_iterator_t*);
|
||||
LEVELDB_EXPORT void leveldb_iter_seek(leveldb_iterator_t*, const char* k,
|
||||
size_t klen);
|
||||
LEVELDB_EXPORT void leveldb_iter_next(leveldb_iterator_t*);
|
||||
LEVELDB_EXPORT void leveldb_iter_prev(leveldb_iterator_t*);
|
||||
LEVELDB_EXPORT const char* leveldb_iter_key(const leveldb_iterator_t*,
|
||||
size_t* klen);
|
||||
LEVELDB_EXPORT const char* leveldb_iter_value(const leveldb_iterator_t*,
|
||||
size_t* vlen);
|
||||
LEVELDB_EXPORT void leveldb_iter_get_error(const leveldb_iterator_t*,
|
||||
char** errptr);
|
||||
|
||||
/* Write batch */
|
||||
|
||||
LEVELDB_EXPORT leveldb_writebatch_t* leveldb_writebatch_create(void);
|
||||
LEVELDB_EXPORT void leveldb_writebatch_destroy(leveldb_writebatch_t*);
|
||||
LEVELDB_EXPORT void leveldb_writebatch_clear(leveldb_writebatch_t*);
|
||||
LEVELDB_EXPORT void leveldb_writebatch_put(leveldb_writebatch_t*,
|
||||
const char* key, size_t klen,
|
||||
const char* val, size_t vlen);
|
||||
LEVELDB_EXPORT void leveldb_writebatch_delete(leveldb_writebatch_t*,
|
||||
const char* key, size_t klen);
|
||||
LEVELDB_EXPORT void leveldb_writebatch_iterate(
|
||||
const leveldb_writebatch_t*, void* state,
|
||||
void (*put)(void*, const char* k, size_t klen, const char* v, size_t vlen),
|
||||
void (*deleted)(void*, const char* k, size_t klen));
|
||||
LEVELDB_EXPORT void leveldb_writebatch_append(
|
||||
leveldb_writebatch_t* destination, const leveldb_writebatch_t* source);
|
||||
|
||||
/* Options */
|
||||
|
||||
LEVELDB_EXPORT leveldb_options_t* leveldb_options_create(void);
|
||||
LEVELDB_EXPORT void leveldb_options_destroy(leveldb_options_t*);
|
||||
LEVELDB_EXPORT void leveldb_options_set_comparator(leveldb_options_t*,
|
||||
leveldb_comparator_t*);
|
||||
LEVELDB_EXPORT void leveldb_options_set_filter_policy(leveldb_options_t*,
|
||||
leveldb_filterpolicy_t*);
|
||||
LEVELDB_EXPORT void leveldb_options_set_create_if_missing(leveldb_options_t*,
|
||||
uint8_t);
|
||||
LEVELDB_EXPORT void leveldb_options_set_error_if_exists(leveldb_options_t*,
|
||||
uint8_t);
|
||||
LEVELDB_EXPORT void leveldb_options_set_paranoid_checks(leveldb_options_t*,
|
||||
uint8_t);
|
||||
LEVELDB_EXPORT void leveldb_options_set_env(leveldb_options_t*, leveldb_env_t*);
|
||||
LEVELDB_EXPORT void leveldb_options_set_info_log(leveldb_options_t*,
|
||||
leveldb_logger_t*);
|
||||
LEVELDB_EXPORT void leveldb_options_set_write_buffer_size(leveldb_options_t*,
|
||||
size_t);
|
||||
LEVELDB_EXPORT void leveldb_options_set_max_open_files(leveldb_options_t*, int);
|
||||
LEVELDB_EXPORT void leveldb_options_set_cache(leveldb_options_t*,
|
||||
leveldb_cache_t*);
|
||||
LEVELDB_EXPORT void leveldb_options_set_block_size(leveldb_options_t*, size_t);
|
||||
LEVELDB_EXPORT void leveldb_options_set_block_restart_interval(
|
||||
leveldb_options_t*, int);
|
||||
LEVELDB_EXPORT void leveldb_options_set_max_file_size(leveldb_options_t*,
|
||||
size_t);
|
||||
|
||||
enum { leveldb_no_compression = 0, leveldb_snappy_compression = 1 };
|
||||
LEVELDB_EXPORT void leveldb_options_set_compression(leveldb_options_t*, int);
|
||||
|
||||
/* Comparator */
|
||||
|
||||
LEVELDB_EXPORT 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_EXPORT void leveldb_comparator_destroy(leveldb_comparator_t*);
|
||||
|
||||
/* Filter policy */
|
||||
|
||||
LEVELDB_EXPORT leveldb_filterpolicy_t* leveldb_filterpolicy_create(
|
||||
void* state, void (*destructor)(void*),
|
||||
char* (*create_filter)(void*, const char* const* key_array,
|
||||
const size_t* key_length_array, int num_keys,
|
||||
size_t* filter_length),
|
||||
uint8_t (*key_may_match)(void*, const char* key, size_t length,
|
||||
const char* filter, size_t filter_length),
|
||||
const char* (*name)(void*));
|
||||
LEVELDB_EXPORT void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t*);
|
||||
|
||||
LEVELDB_EXPORT leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(
|
||||
int bits_per_key);
|
||||
|
||||
/* Read options */
|
||||
|
||||
LEVELDB_EXPORT leveldb_readoptions_t* leveldb_readoptions_create(void);
|
||||
LEVELDB_EXPORT void leveldb_readoptions_destroy(leveldb_readoptions_t*);
|
||||
LEVELDB_EXPORT void leveldb_readoptions_set_verify_checksums(
|
||||
leveldb_readoptions_t*, uint8_t);
|
||||
LEVELDB_EXPORT void leveldb_readoptions_set_fill_cache(leveldb_readoptions_t*,
|
||||
uint8_t);
|
||||
LEVELDB_EXPORT void leveldb_readoptions_set_snapshot(leveldb_readoptions_t*,
|
||||
const leveldb_snapshot_t*);
|
||||
|
||||
/* Write options */
|
||||
|
||||
LEVELDB_EXPORT leveldb_writeoptions_t* leveldb_writeoptions_create(void);
|
||||
LEVELDB_EXPORT void leveldb_writeoptions_destroy(leveldb_writeoptions_t*);
|
||||
LEVELDB_EXPORT void leveldb_writeoptions_set_sync(leveldb_writeoptions_t*,
|
||||
uint8_t);
|
||||
|
||||
/* Cache */
|
||||
|
||||
LEVELDB_EXPORT leveldb_cache_t* leveldb_cache_create_lru(size_t capacity);
|
||||
LEVELDB_EXPORT void leveldb_cache_destroy(leveldb_cache_t* cache);
|
||||
|
||||
/* Env */
|
||||
|
||||
LEVELDB_EXPORT leveldb_env_t* leveldb_create_default_env(void);
|
||||
LEVELDB_EXPORT void leveldb_env_destroy(leveldb_env_t*);
|
||||
|
||||
/* If not NULL, the returned buffer must be released using leveldb_free(). */
|
||||
LEVELDB_EXPORT char* leveldb_env_get_test_directory(leveldb_env_t*);
|
||||
|
||||
/* Utility */
|
||||
|
||||
/* Calls free(ptr).
|
||||
REQUIRES: ptr was malloc()-ed and returned by one of the routines
|
||||
in this file. Note that in certain cases (typically on Windows), you
|
||||
may need to call this routine instead of free(ptr) to dispose of
|
||||
malloc()-ed memory returned by this library. */
|
||||
LEVELDB_EXPORT void leveldb_free(void* ptr);
|
||||
|
||||
/* Return the major version number for this release. */
|
||||
LEVELDB_EXPORT int leveldb_major_version(void);
|
||||
|
||||
/* Return the minor version number for this release. */
|
||||
LEVELDB_EXPORT int leveldb_minor_version(void);
|
||||
|
||||
#ifdef __cplusplus
|
||||
} /* end extern "C" */
|
||||
#endif
|
||||
|
||||
#endif /* STORAGE_LEVELDB_INCLUDE_C_H_ */
|
||||
103
3rdparty/leveldb/cache.h
vendored
Normal file
103
3rdparty/leveldb/cache.h
vendored
Normal file
@@ -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.
|
||||
//
|
||||
// A Cache is an interface that maps keys to values. It has internal
|
||||
// synchronization and may be safely accessed concurrently from
|
||||
// multiple threads. It may automatically evict entries to make room
|
||||
// for new entries. Values have a specified charge against the cache
|
||||
// capacity. For example, a cache where the values are variable
|
||||
// length strings, may use the length of the string as the charge for
|
||||
// the string.
|
||||
//
|
||||
// A builtin cache implementation with a least-recently-used eviction
|
||||
// policy is provided. Clients may use their own implementations if
|
||||
// they want something more sophisticated (like scan-resistance, a
|
||||
// custom eviction policy, variable cache sizing, etc.)
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_CACHE_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_CACHE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
#include "leveldb/slice.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class LEVELDB_EXPORT Cache;
|
||||
|
||||
// Create a new cache with a fixed size capacity. This implementation
|
||||
// of Cache uses a least-recently-used eviction policy.
|
||||
LEVELDB_EXPORT Cache* NewLRUCache(size_t capacity);
|
||||
|
||||
class LEVELDB_EXPORT Cache {
|
||||
public:
|
||||
Cache() = default;
|
||||
|
||||
Cache(const Cache&) = delete;
|
||||
Cache& operator=(const Cache&) = delete;
|
||||
|
||||
// Destroys all existing entries by calling the "deleter"
|
||||
// function that was passed to the constructor.
|
||||
virtual ~Cache();
|
||||
|
||||
// Opaque handle to an entry stored in the cache.
|
||||
struct Handle {};
|
||||
|
||||
// Insert a mapping from key->value into the cache and assign it
|
||||
// the specified charge against the total cache capacity.
|
||||
//
|
||||
// Returns a handle that corresponds to the mapping. The caller
|
||||
// must call this->Release(handle) when the returned mapping is no
|
||||
// longer needed.
|
||||
//
|
||||
// When the inserted entry is no longer needed, the key and
|
||||
// value will be passed to "deleter".
|
||||
virtual Handle* Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value)) = 0;
|
||||
|
||||
// If the cache has no mapping for "key", returns nullptr.
|
||||
//
|
||||
// Else return a handle that corresponds to the mapping. The caller
|
||||
// must call this->Release(handle) when the returned mapping is no
|
||||
// longer needed.
|
||||
virtual Handle* Lookup(const Slice& key) = 0;
|
||||
|
||||
// Release a mapping returned by a previous Lookup().
|
||||
// REQUIRES: handle must not have been released yet.
|
||||
// REQUIRES: handle must have been returned by a method on *this.
|
||||
virtual void Release(Handle* handle) = 0;
|
||||
|
||||
// Return the value encapsulated in a handle returned by a
|
||||
// successful Lookup().
|
||||
// REQUIRES: handle must not have been released yet.
|
||||
// REQUIRES: handle must have been returned by a method on *this.
|
||||
virtual void* Value(Handle* handle) = 0;
|
||||
|
||||
// If the cache contains entry for key, erase it. Note that the
|
||||
// underlying entry will be kept around until all existing handles
|
||||
// to it have been released.
|
||||
virtual void Erase(const Slice& key) = 0;
|
||||
|
||||
// Return a new numeric id. May be used by multiple clients who are
|
||||
// sharing the same cache to partition the key space. Typically the
|
||||
// client will allocate a new id at startup and prepend the id to
|
||||
// its cache keys.
|
||||
virtual uint64_t NewId() = 0;
|
||||
|
||||
// Remove all cache entries that are not actively in use. Memory-constrained
|
||||
// applications may wish to call this method to reduce memory usage.
|
||||
// Default implementation of Prune() does nothing. Subclasses are strongly
|
||||
// encouraged to override the default implementation. A future release of
|
||||
// leveldb may change Prune() to a pure abstract method.
|
||||
virtual void Prune() {}
|
||||
|
||||
// Return an estimate of the combined charges of all elements stored in the
|
||||
// cache.
|
||||
virtual size_t TotalCharge() const = 0;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_CACHE_H_
|
||||
64
3rdparty/leveldb/comparator.h
vendored
Normal file
64
3rdparty/leveldb/comparator.h
vendored
Normal file
@@ -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.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class Slice;
|
||||
|
||||
// A Comparator object provides a total order across slices that are
|
||||
// used as keys in an sstable or a database. A Comparator implementation
|
||||
// must be thread-safe since leveldb may invoke its methods concurrently
|
||||
// from multiple threads.
|
||||
class LEVELDB_EXPORT Comparator {
|
||||
public:
|
||||
virtual ~Comparator();
|
||||
|
||||
// Three-way comparison. Returns value:
|
||||
// < 0 iff "a" < "b",
|
||||
// == 0 iff "a" == "b",
|
||||
// > 0 iff "a" > "b"
|
||||
virtual int Compare(const Slice& a, const Slice& b) const = 0;
|
||||
|
||||
// The name of the comparator. Used to check for comparator
|
||||
// mismatches (i.e., a DB created with one comparator is
|
||||
// accessed using a different comparator.
|
||||
//
|
||||
// The client of this package should switch to a new name whenever
|
||||
// the comparator implementation changes in a way that will cause
|
||||
// the relative ordering of any two keys to change.
|
||||
//
|
||||
// Names starting with "leveldb." are reserved and should not be used
|
||||
// by any clients of this package.
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
// Advanced functions: these are used to reduce the space requirements
|
||||
// for internal data structures like index blocks.
|
||||
|
||||
// If *start < limit, changes *start to a short string in [start,limit).
|
||||
// Simple comparator implementations may return with *start unchanged,
|
||||
// i.e., an implementation of this method that does nothing is correct.
|
||||
virtual void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const = 0;
|
||||
|
||||
// Changes *key to a short string >= *key.
|
||||
// Simple comparator implementations may return with *key unchanged,
|
||||
// i.e., an implementation of this method that does nothing is correct.
|
||||
virtual void FindShortSuccessor(std::string* key) const = 0;
|
||||
};
|
||||
|
||||
// Return a builtin comparator that uses lexicographic byte-wise
|
||||
// ordering. The result remains the property of this module and
|
||||
// must not be deleted.
|
||||
LEVELDB_EXPORT const Comparator* BytewiseComparator();
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_COMPARATOR_H_
|
||||
167
3rdparty/leveldb/db.h
vendored
Normal file
167
3rdparty/leveldb/db.h
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
// 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_INCLUDE_DB_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_DB_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
#include "leveldb/iterator.h"
|
||||
#include "leveldb/options.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
// Update CMakeLists.txt if you change these
|
||||
static const int kMajorVersion = 1;
|
||||
static const int kMinorVersion = 23;
|
||||
|
||||
struct Options;
|
||||
struct ReadOptions;
|
||||
struct WriteOptions;
|
||||
class WriteBatch;
|
||||
|
||||
// Abstract handle to particular state of a DB.
|
||||
// A Snapshot is an immutable object and can therefore be safely
|
||||
// accessed from multiple threads without any external synchronization.
|
||||
class LEVELDB_EXPORT Snapshot {
|
||||
protected:
|
||||
virtual ~Snapshot();
|
||||
};
|
||||
|
||||
// A range of keys
|
||||
struct LEVELDB_EXPORT Range {
|
||||
Range() = default;
|
||||
Range(const Slice& s, const Slice& l) : start(s), limit(l) {}
|
||||
|
||||
Slice start; // Included in the range
|
||||
Slice limit; // Not included in the range
|
||||
};
|
||||
|
||||
// A DB is a persistent ordered map from keys to values.
|
||||
// A DB is safe for concurrent access from multiple threads without
|
||||
// any external synchronization.
|
||||
class LEVELDB_EXPORT DB {
|
||||
public:
|
||||
// Open the database with the specified "name".
|
||||
// Stores a pointer to a heap-allocated database in *dbptr and returns
|
||||
// OK on success.
|
||||
// Stores nullptr in *dbptr and returns a non-OK status on error.
|
||||
// Caller should delete *dbptr when it is no longer needed.
|
||||
static Status Open(const Options& options, const std::string& name,
|
||||
DB** dbptr);
|
||||
|
||||
DB() = default;
|
||||
|
||||
DB(const DB&) = delete;
|
||||
DB& operator=(const DB&) = delete;
|
||||
|
||||
virtual ~DB();
|
||||
|
||||
// Set the database entry for "key" to "value". Returns OK on success,
|
||||
// and a non-OK status on error.
|
||||
// Note: consider setting options.sync = true.
|
||||
virtual Status Put(const WriteOptions& options, const Slice& key,
|
||||
const Slice& value) = 0;
|
||||
|
||||
// Remove the database entry (if any) for "key". Returns OK on
|
||||
// success, and a non-OK status on error. It is not an error if "key"
|
||||
// did not exist in the database.
|
||||
// Note: consider setting options.sync = true.
|
||||
virtual Status Delete(const WriteOptions& options, const Slice& key) = 0;
|
||||
|
||||
// Apply the specified updates to the database.
|
||||
// Returns OK on success, non-OK on failure.
|
||||
// Note: consider setting options.sync = true.
|
||||
virtual Status Write(const WriteOptions& options, WriteBatch* updates) = 0;
|
||||
|
||||
// If the database contains an entry for "key" store the
|
||||
// corresponding value in *value and return OK.
|
||||
//
|
||||
// If there is no entry for "key" leave *value unchanged and return
|
||||
// a status for which Status::IsNotFound() returns true.
|
||||
//
|
||||
// May return some other Status on an error.
|
||||
virtual Status Get(const ReadOptions& options, const Slice& key,
|
||||
std::string* value) = 0;
|
||||
|
||||
// Return a heap-allocated iterator over the contents of the database.
|
||||
// The result of NewIterator() is initially invalid (caller must
|
||||
// call one of the Seek methods on the iterator before using it).
|
||||
//
|
||||
// Caller should delete the iterator when it is no longer needed.
|
||||
// The returned iterator should be deleted before this db is deleted.
|
||||
virtual Iterator* NewIterator(const ReadOptions& options) = 0;
|
||||
|
||||
// Return a handle to the current DB state. Iterators created with
|
||||
// this handle will all observe a stable snapshot of the current DB
|
||||
// state. The caller must call ReleaseSnapshot(result) when the
|
||||
// snapshot is no longer needed.
|
||||
virtual const Snapshot* GetSnapshot() = 0;
|
||||
|
||||
// Release a previously acquired snapshot. The caller must not
|
||||
// use "snapshot" after this call.
|
||||
virtual void ReleaseSnapshot(const Snapshot* snapshot) = 0;
|
||||
|
||||
// DB implementations can export properties about their state
|
||||
// via this method. If "property" is a valid property understood by this
|
||||
// DB implementation, fills "*value" with its current value and returns
|
||||
// true. Otherwise returns false.
|
||||
//
|
||||
//
|
||||
// Valid property names include:
|
||||
//
|
||||
// "leveldb.num-files-at-level<N>" - return the number of files at level <N>,
|
||||
// where <N> is an ASCII representation of a level number (e.g. "0").
|
||||
// "leveldb.stats" - returns a multi-line string that describes statistics
|
||||
// about the internal operation of the DB.
|
||||
// "leveldb.sstables" - returns a multi-line string that describes all
|
||||
// of the sstables that make up the db contents.
|
||||
// "leveldb.approximate-memory-usage" - returns the approximate number of
|
||||
// bytes of memory in use by the DB.
|
||||
virtual bool GetProperty(const Slice& property, std::string* value) = 0;
|
||||
|
||||
// For each i in [0,n-1], store in "sizes[i]", the approximate
|
||||
// file system space used by keys in "[range[i].start .. range[i].limit)".
|
||||
//
|
||||
// Note that the returned sizes measure file system space usage, so
|
||||
// if the user data compresses by a factor of ten, the returned
|
||||
// sizes will be one-tenth the size of the corresponding user data size.
|
||||
//
|
||||
// The results may not include the sizes of recently written data.
|
||||
virtual void GetApproximateSizes(const Range* range, int n,
|
||||
uint64_t* sizes) = 0;
|
||||
|
||||
// Compact the underlying storage for the key range [*begin,*end].
|
||||
// In particular, deleted and overwritten versions are discarded,
|
||||
// and the data is rearranged to reduce the cost of operations
|
||||
// needed to access the data. This operation should typically only
|
||||
// be invoked by users who understand the underlying implementation.
|
||||
//
|
||||
// begin==nullptr is treated as a key before all keys in the database.
|
||||
// end==nullptr is treated as a key after all keys in the database.
|
||||
// Therefore the following call will compact the entire database:
|
||||
// db->CompactRange(nullptr, nullptr);
|
||||
virtual void CompactRange(const Slice* begin, const Slice* end) = 0;
|
||||
};
|
||||
|
||||
// Destroy the contents of the specified database.
|
||||
// Be very careful using this method.
|
||||
//
|
||||
// Note: For backwards compatibility, if DestroyDB is unable to list the
|
||||
// database files, Status::OK() will still be returned masking this failure.
|
||||
LEVELDB_EXPORT Status DestroyDB(const std::string& name,
|
||||
const Options& options);
|
||||
|
||||
// If a DB cannot be opened, you may attempt to call this method to
|
||||
// resurrect as much of the contents of the database as possible.
|
||||
// Some data may be lost, so be careful when calling this function
|
||||
// on a database that contains important information.
|
||||
LEVELDB_EXPORT Status RepairDB(const std::string& dbname,
|
||||
const Options& options);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_DB_H_
|
||||
28
3rdparty/leveldb/dumpfile.h
vendored
Normal file
28
3rdparty/leveldb/dumpfile.h
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright (c) 2014 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_INCLUDE_DUMPFILE_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/export.h"
|
||||
#include "leveldb/status.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
// Dump the contents of the file named by fname in text format to
|
||||
// *dst. Makes a sequence of dst->Append() calls; each call is passed
|
||||
// the newline-terminated text corresponding to a single item found
|
||||
// in the file.
|
||||
//
|
||||
// Returns a non-OK result if fname does not name a leveldb storage
|
||||
// file, or if the file cannot be read.
|
||||
LEVELDB_EXPORT Status DumpFile(Env* env, const std::string& fname,
|
||||
WritableFile* dst);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_DUMPFILE_H_
|
||||
417
3rdparty/leveldb/env.h
vendored
Normal file
417
3rdparty/leveldb/env.h
vendored
Normal file
@@ -0,0 +1,417 @@
|
||||
// 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.
|
||||
//
|
||||
// An Env is an interface used by the leveldb implementation to access
|
||||
// operating system functionality like the filesystem etc. Callers
|
||||
// may wish to provide a custom Env object when opening a database to
|
||||
// get fine gain control; e.g., to rate limit file system operations.
|
||||
//
|
||||
// All Env implementations are safe for concurrent access from
|
||||
// multiple threads without any external synchronization.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_ENV_H_
|
||||
|
||||
#include <cstdarg>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
#include "leveldb/status.h"
|
||||
|
||||
// This workaround can be removed when leveldb::Env::DeleteFile is removed.
|
||||
#if defined(_WIN32)
|
||||
// On Windows, the method name DeleteFile (below) introduces the risk of
|
||||
// triggering undefined behavior by exposing the compiler to different
|
||||
// declarations of the Env class in different translation units.
|
||||
//
|
||||
// This is because <windows.h>, a fairly popular header file for Windows
|
||||
// applications, defines a DeleteFile macro. So, files that include the Windows
|
||||
// header before this header will contain an altered Env declaration.
|
||||
//
|
||||
// This workaround ensures that the compiler sees the same Env declaration,
|
||||
// independently of whether <windows.h> was included.
|
||||
#if defined(DeleteFile)
|
||||
#undef DeleteFile
|
||||
#define LEVELDB_DELETEFILE_UNDEFINED
|
||||
#endif // defined(DeleteFile)
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class FileLock;
|
||||
class Logger;
|
||||
class RandomAccessFile;
|
||||
class SequentialFile;
|
||||
class Slice;
|
||||
class WritableFile;
|
||||
|
||||
class LEVELDB_EXPORT Env {
|
||||
public:
|
||||
Env();
|
||||
|
||||
Env(const Env&) = delete;
|
||||
Env& operator=(const Env&) = delete;
|
||||
|
||||
virtual ~Env();
|
||||
|
||||
// Return a default environment suitable for the current operating
|
||||
// system. Sophisticated users may wish to provide their own Env
|
||||
// implementation instead of relying on this default environment.
|
||||
//
|
||||
// The result of Default() belongs to leveldb and must never be deleted.
|
||||
static Env* Default();
|
||||
|
||||
// Create an object that sequentially reads the file with the specified name.
|
||||
// On success, stores a pointer to the new file in *result and returns OK.
|
||||
// On failure stores nullptr in *result and returns non-OK. If the file does
|
||||
// not exist, returns a non-OK status. Implementations should return a
|
||||
// NotFound status when the file does not exist.
|
||||
//
|
||||
// The returned file will only be accessed by one thread at a time.
|
||||
virtual Status NewSequentialFile(const std::string& fname,
|
||||
SequentialFile** result) = 0;
|
||||
|
||||
// Create an object supporting random-access reads from the file with the
|
||||
// specified name. On success, stores a pointer to the new file in
|
||||
// *result and returns OK. On failure stores nullptr in *result and
|
||||
// returns non-OK. If the file does not exist, returns a non-OK
|
||||
// status. Implementations should return a NotFound status when the file does
|
||||
// not exist.
|
||||
//
|
||||
// The returned file may be concurrently accessed by multiple threads.
|
||||
virtual Status NewRandomAccessFile(const std::string& fname,
|
||||
RandomAccessFile** result) = 0;
|
||||
|
||||
// Create an object that writes to a new file with the specified
|
||||
// name. Deletes any existing file with the same name and creates a
|
||||
// new file. On success, stores a pointer to the new file in
|
||||
// *result and returns OK. On failure stores nullptr in *result and
|
||||
// returns non-OK.
|
||||
//
|
||||
// The returned file will only be accessed by one thread at a time.
|
||||
virtual Status NewWritableFile(const std::string& fname,
|
||||
WritableFile** result) = 0;
|
||||
|
||||
// Create an object that either appends to an existing file, or
|
||||
// writes to a new file (if the file does not exist to begin with).
|
||||
// On success, stores a pointer to the new file in *result and
|
||||
// returns OK. On failure stores nullptr in *result and returns
|
||||
// non-OK.
|
||||
//
|
||||
// The returned file will only be accessed by one thread at a time.
|
||||
//
|
||||
// May return an IsNotSupportedError error if this Env does
|
||||
// not allow appending to an existing file. Users of Env (including
|
||||
// the leveldb implementation) must be prepared to deal with
|
||||
// an Env that does not support appending.
|
||||
virtual Status NewAppendableFile(const std::string& fname,
|
||||
WritableFile** result);
|
||||
|
||||
// Returns true iff the named file exists.
|
||||
virtual bool FileExists(const std::string& fname) = 0;
|
||||
|
||||
// Store in *result the names of the children of the specified directory.
|
||||
// The names are relative to "dir".
|
||||
// Original contents of *results are dropped.
|
||||
virtual Status GetChildren(const std::string& dir,
|
||||
std::vector<std::string>* result) = 0;
|
||||
// Delete the named file.
|
||||
//
|
||||
// The default implementation calls DeleteFile, to support legacy Env
|
||||
// implementations. Updated Env implementations must override RemoveFile and
|
||||
// ignore the existence of DeleteFile. Updated code calling into the Env API
|
||||
// must call RemoveFile instead of DeleteFile.
|
||||
//
|
||||
// A future release will remove DeleteDir and the default implementation of
|
||||
// RemoveDir.
|
||||
virtual Status RemoveFile(const std::string& fname);
|
||||
|
||||
// DEPRECATED: Modern Env implementations should override RemoveFile instead.
|
||||
//
|
||||
// The default implementation calls RemoveFile, to support legacy Env user
|
||||
// code that calls this method on modern Env implementations. Modern Env user
|
||||
// code should call RemoveFile.
|
||||
//
|
||||
// A future release will remove this method.
|
||||
virtual Status DeleteFile(const std::string& fname);
|
||||
|
||||
// Create the specified directory.
|
||||
virtual Status CreateDir(const std::string& dirname) = 0;
|
||||
|
||||
// Delete the specified directory.
|
||||
//
|
||||
// The default implementation calls DeleteDir, to support legacy Env
|
||||
// implementations. Updated Env implementations must override RemoveDir and
|
||||
// ignore the existence of DeleteDir. Modern code calling into the Env API
|
||||
// must call RemoveDir instead of DeleteDir.
|
||||
//
|
||||
// A future release will remove DeleteDir and the default implementation of
|
||||
// RemoveDir.
|
||||
virtual Status RemoveDir(const std::string& dirname);
|
||||
|
||||
// DEPRECATED: Modern Env implementations should override RemoveDir instead.
|
||||
//
|
||||
// The default implementation calls RemoveDir, to support legacy Env user
|
||||
// code that calls this method on modern Env implementations. Modern Env user
|
||||
// code should call RemoveDir.
|
||||
//
|
||||
// A future release will remove this method.
|
||||
virtual Status DeleteDir(const std::string& dirname);
|
||||
|
||||
// Store the size of fname in *file_size.
|
||||
virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0;
|
||||
|
||||
// Rename file src to target.
|
||||
virtual Status RenameFile(const std::string& src,
|
||||
const std::string& target) = 0;
|
||||
|
||||
// Lock the specified file. Used to prevent concurrent access to
|
||||
// the same db by multiple processes. On failure, stores nullptr in
|
||||
// *lock and returns non-OK.
|
||||
//
|
||||
// On success, stores a pointer to the object that represents the
|
||||
// acquired lock in *lock and returns OK. The caller should call
|
||||
// UnlockFile(*lock) to release the lock. If the process exits,
|
||||
// the lock will be automatically released.
|
||||
//
|
||||
// If somebody else already holds the lock, finishes immediately
|
||||
// with a failure. I.e., this call does not wait for existing locks
|
||||
// to go away.
|
||||
//
|
||||
// May create the named file if it does not already exist.
|
||||
virtual Status LockFile(const std::string& fname, FileLock** lock) = 0;
|
||||
|
||||
// Release the lock acquired by a previous successful call to LockFile.
|
||||
// REQUIRES: lock was returned by a successful LockFile() call
|
||||
// REQUIRES: lock has not already been unlocked.
|
||||
virtual Status UnlockFile(FileLock* lock) = 0;
|
||||
|
||||
// Arrange to run "(*function)(arg)" once in a background thread.
|
||||
//
|
||||
// "function" may run in an unspecified thread. Multiple functions
|
||||
// added to the same Env may run concurrently in different threads.
|
||||
// I.e., the caller may not assume that background work items are
|
||||
// serialized.
|
||||
virtual void Schedule(void (*function)(void* arg), void* arg) = 0;
|
||||
|
||||
// Start a new thread, invoking "function(arg)" within the new thread.
|
||||
// When "function(arg)" returns, the thread will be destroyed.
|
||||
virtual void StartThread(void (*function)(void* arg), void* arg) = 0;
|
||||
|
||||
// *path is set to a temporary directory that can be used for testing. It may
|
||||
// or may not have just been created. The directory may or may not differ
|
||||
// between runs of the same process, but subsequent calls will return the
|
||||
// same directory.
|
||||
virtual Status GetTestDirectory(std::string* path) = 0;
|
||||
|
||||
// Create and return a log file for storing informational messages.
|
||||
virtual Status NewLogger(const std::string& fname, Logger** result) = 0;
|
||||
|
||||
// Returns the number of micro-seconds since some fixed point in time. Only
|
||||
// useful for computing deltas of time.
|
||||
virtual uint64_t NowMicros() = 0;
|
||||
|
||||
// Sleep/delay the thread for the prescribed number of micro-seconds.
|
||||
virtual void SleepForMicroseconds(int micros) = 0;
|
||||
};
|
||||
|
||||
// A file abstraction for reading sequentially through a file
|
||||
class LEVELDB_EXPORT SequentialFile {
|
||||
public:
|
||||
SequentialFile() = default;
|
||||
|
||||
SequentialFile(const SequentialFile&) = delete;
|
||||
SequentialFile& operator=(const SequentialFile&) = delete;
|
||||
|
||||
virtual ~SequentialFile();
|
||||
|
||||
// Read up to "n" bytes from the file. "scratch[0..n-1]" may be
|
||||
// written by this routine. Sets "*result" to the data that was
|
||||
// read (including if fewer than "n" bytes were successfully read).
|
||||
// May set "*result" to point at data in "scratch[0..n-1]", so
|
||||
// "scratch[0..n-1]" must be live when "*result" is used.
|
||||
// If an error was encountered, returns a non-OK status.
|
||||
//
|
||||
// REQUIRES: External synchronization
|
||||
virtual Status Read(size_t n, Slice* result, char* scratch) = 0;
|
||||
|
||||
// Skip "n" bytes from the file. This is guaranteed to be no
|
||||
// slower that reading the same data, but may be faster.
|
||||
//
|
||||
// If end of file is reached, skipping will stop at the end of the
|
||||
// file, and Skip will return OK.
|
||||
//
|
||||
// REQUIRES: External synchronization
|
||||
virtual Status Skip(uint64_t n) = 0;
|
||||
};
|
||||
|
||||
// A file abstraction for randomly reading the contents of a file.
|
||||
class LEVELDB_EXPORT RandomAccessFile {
|
||||
public:
|
||||
RandomAccessFile() = default;
|
||||
|
||||
RandomAccessFile(const RandomAccessFile&) = delete;
|
||||
RandomAccessFile& operator=(const RandomAccessFile&) = delete;
|
||||
|
||||
virtual ~RandomAccessFile();
|
||||
|
||||
// Read up to "n" bytes from the file starting at "offset".
|
||||
// "scratch[0..n-1]" may be written by this routine. Sets "*result"
|
||||
// to the data that was read (including if fewer than "n" bytes were
|
||||
// successfully read). May set "*result" to point at data in
|
||||
// "scratch[0..n-1]", so "scratch[0..n-1]" must be live when
|
||||
// "*result" is used. If an error was encountered, returns a non-OK
|
||||
// status.
|
||||
//
|
||||
// Safe for concurrent use by multiple threads.
|
||||
virtual Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const = 0;
|
||||
};
|
||||
|
||||
// A file abstraction for sequential writing. The implementation
|
||||
// must provide buffering since callers may append small fragments
|
||||
// at a time to the file.
|
||||
class LEVELDB_EXPORT WritableFile {
|
||||
public:
|
||||
WritableFile() = default;
|
||||
|
||||
WritableFile(const WritableFile&) = delete;
|
||||
WritableFile& operator=(const WritableFile&) = delete;
|
||||
|
||||
virtual ~WritableFile();
|
||||
|
||||
virtual Status Append(const Slice& data) = 0;
|
||||
virtual Status Close() = 0;
|
||||
virtual Status Flush() = 0;
|
||||
virtual Status Sync() = 0;
|
||||
};
|
||||
|
||||
// An interface for writing log messages.
|
||||
class LEVELDB_EXPORT Logger {
|
||||
public:
|
||||
Logger() = default;
|
||||
|
||||
Logger(const Logger&) = delete;
|
||||
Logger& operator=(const Logger&) = delete;
|
||||
|
||||
virtual ~Logger();
|
||||
|
||||
// Write an entry to the log file with the specified format.
|
||||
virtual void Logv(const char* format, std::va_list ap) = 0;
|
||||
};
|
||||
|
||||
// Identifies a locked file.
|
||||
class LEVELDB_EXPORT FileLock {
|
||||
public:
|
||||
FileLock() = default;
|
||||
|
||||
FileLock(const FileLock&) = delete;
|
||||
FileLock& operator=(const FileLock&) = delete;
|
||||
|
||||
virtual ~FileLock();
|
||||
};
|
||||
|
||||
// Log the specified data to *info_log if info_log is non-null.
|
||||
void Log(Logger* info_log, const char* format, ...)
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
__attribute__((__format__(__printf__, 2, 3)))
|
||||
#endif
|
||||
;
|
||||
|
||||
// A utility routine: write "data" to the named file.
|
||||
LEVELDB_EXPORT Status WriteStringToFile(Env* env, const Slice& data,
|
||||
const std::string& fname);
|
||||
|
||||
// A utility routine: read contents of named file into *data
|
||||
LEVELDB_EXPORT Status ReadFileToString(Env* env, const std::string& fname,
|
||||
std::string* data);
|
||||
|
||||
// An implementation of Env that forwards all calls to another Env.
|
||||
// May be useful to clients who wish to override just part of the
|
||||
// functionality of another Env.
|
||||
class LEVELDB_EXPORT EnvWrapper : public Env {
|
||||
public:
|
||||
// Initialize an EnvWrapper that delegates all calls to *t.
|
||||
explicit EnvWrapper(Env* t) : target_(t) {}
|
||||
virtual ~EnvWrapper();
|
||||
|
||||
// Return the target to which this Env forwards all calls.
|
||||
Env* target() const { return target_; }
|
||||
|
||||
// The following text is boilerplate that forwards all methods to target().
|
||||
Status NewSequentialFile(const std::string& f, SequentialFile** r) override {
|
||||
return target_->NewSequentialFile(f, r);
|
||||
}
|
||||
Status NewRandomAccessFile(const std::string& f,
|
||||
RandomAccessFile** r) override {
|
||||
return target_->NewRandomAccessFile(f, r);
|
||||
}
|
||||
Status NewWritableFile(const std::string& f, WritableFile** r) override {
|
||||
return target_->NewWritableFile(f, r);
|
||||
}
|
||||
Status NewAppendableFile(const std::string& f, WritableFile** r) override {
|
||||
return target_->NewAppendableFile(f, r);
|
||||
}
|
||||
bool FileExists(const std::string& f) override {
|
||||
return target_->FileExists(f);
|
||||
}
|
||||
Status GetChildren(const std::string& dir,
|
||||
std::vector<std::string>* r) override {
|
||||
return target_->GetChildren(dir, r);
|
||||
}
|
||||
Status RemoveFile(const std::string& f) override {
|
||||
return target_->RemoveFile(f);
|
||||
}
|
||||
Status CreateDir(const std::string& d) override {
|
||||
return target_->CreateDir(d);
|
||||
}
|
||||
Status RemoveDir(const std::string& d) override {
|
||||
return target_->RemoveDir(d);
|
||||
}
|
||||
Status GetFileSize(const std::string& f, uint64_t* s) override {
|
||||
return target_->GetFileSize(f, s);
|
||||
}
|
||||
Status RenameFile(const std::string& s, const std::string& t) override {
|
||||
return target_->RenameFile(s, t);
|
||||
}
|
||||
Status LockFile(const std::string& f, FileLock** l) override {
|
||||
return target_->LockFile(f, l);
|
||||
}
|
||||
Status UnlockFile(FileLock* l) override { return target_->UnlockFile(l); }
|
||||
void Schedule(void (*f)(void*), void* a) override {
|
||||
return target_->Schedule(f, a);
|
||||
}
|
||||
void StartThread(void (*f)(void*), void* a) override {
|
||||
return target_->StartThread(f, a);
|
||||
}
|
||||
Status GetTestDirectory(std::string* path) override {
|
||||
return target_->GetTestDirectory(path);
|
||||
}
|
||||
Status NewLogger(const std::string& fname, Logger** result) override {
|
||||
return target_->NewLogger(fname, result);
|
||||
}
|
||||
uint64_t NowMicros() override { return target_->NowMicros(); }
|
||||
void SleepForMicroseconds(int micros) override {
|
||||
target_->SleepForMicroseconds(micros);
|
||||
}
|
||||
|
||||
private:
|
||||
Env* target_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
// This workaround can be removed when leveldb::Env::DeleteFile is removed.
|
||||
// Redefine DeleteFile if it was undefined earlier.
|
||||
#if defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)
|
||||
#if defined(UNICODE)
|
||||
#define DeleteFile DeleteFileW
|
||||
#else
|
||||
#define DeleteFile DeleteFileA
|
||||
#endif // defined(UNICODE)
|
||||
#endif // defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_
|
||||
33
3rdparty/leveldb/export.h
vendored
Normal file
33
3rdparty/leveldb/export.h
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright (c) 2017 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_INCLUDE_EXPORT_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_EXPORT_H_
|
||||
|
||||
#if !defined(LEVELDB_EXPORT)
|
||||
|
||||
#if defined(LEVELDB_SHARED_LIBRARY)
|
||||
#if defined(_WIN32)
|
||||
|
||||
#if defined(LEVELDB_COMPILE_LIBRARY)
|
||||
#define LEVELDB_EXPORT __declspec(dllexport)
|
||||
#else
|
||||
#define LEVELDB_EXPORT __declspec(dllimport)
|
||||
#endif // defined(LEVELDB_COMPILE_LIBRARY)
|
||||
|
||||
#else // defined(_WIN32)
|
||||
#if defined(LEVELDB_COMPILE_LIBRARY)
|
||||
#define LEVELDB_EXPORT __attribute__((visibility("default")))
|
||||
#else
|
||||
#define LEVELDB_EXPORT
|
||||
#endif
|
||||
#endif // defined(_WIN32)
|
||||
|
||||
#else // defined(LEVELDB_SHARED_LIBRARY)
|
||||
#define LEVELDB_EXPORT
|
||||
#endif
|
||||
|
||||
#endif // !defined(LEVELDB_EXPORT)
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_EXPORT_H_
|
||||
72
3rdparty/leveldb/filter_policy.h
vendored
Normal file
72
3rdparty/leveldb/filter_policy.h
vendored
Normal file
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) 2012 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 database can be configured with a custom FilterPolicy object.
|
||||
// This object is responsible for creating a small filter from a set
|
||||
// of keys. These filters are stored in leveldb and are consulted
|
||||
// automatically by leveldb to decide whether or not to read some
|
||||
// information from disk. In many cases, a filter can cut down the
|
||||
// number of disk seeks form a handful to a single disk seek per
|
||||
// DB::Get() call.
|
||||
//
|
||||
// Most people will want to use the builtin bloom filter support (see
|
||||
// NewBloomFilterPolicy() below).
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class Slice;
|
||||
|
||||
class LEVELDB_EXPORT FilterPolicy {
|
||||
public:
|
||||
virtual ~FilterPolicy();
|
||||
|
||||
// Return the name of this policy. Note that if the filter encoding
|
||||
// changes in an incompatible way, the name returned by this method
|
||||
// must be changed. Otherwise, old incompatible filters may be
|
||||
// passed to methods of this type.
|
||||
virtual const char* Name() const = 0;
|
||||
|
||||
// keys[0,n-1] contains a list of keys (potentially with duplicates)
|
||||
// that are ordered according to the user supplied comparator.
|
||||
// Append a filter that summarizes keys[0,n-1] to *dst.
|
||||
//
|
||||
// Warning: do not change the initial contents of *dst. Instead,
|
||||
// append the newly constructed filter to *dst.
|
||||
virtual void CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const = 0;
|
||||
|
||||
// "filter" contains the data appended by a preceding call to
|
||||
// CreateFilter() on this class. This method must return true if
|
||||
// the key was in the list of keys passed to CreateFilter().
|
||||
// This method may return true or false if the key was not on the
|
||||
// list, but it should aim to return false with a high probability.
|
||||
virtual bool KeyMayMatch(const Slice& key, const Slice& filter) const = 0;
|
||||
};
|
||||
|
||||
// Return a new filter policy that uses a bloom filter with approximately
|
||||
// the specified number of bits per key. A good value for bits_per_key
|
||||
// is 10, which yields a filter with ~ 1% false positive rate.
|
||||
//
|
||||
// Callers must delete the result after any database that is using the
|
||||
// result has been closed.
|
||||
//
|
||||
// Note: if you are using a custom comparator that ignores some parts
|
||||
// of the keys being compared, you must not use NewBloomFilterPolicy()
|
||||
// and must provide your own FilterPolicy that also ignores the
|
||||
// corresponding parts of the keys. For example, if the comparator
|
||||
// ignores trailing spaces, it would be incorrect to use a
|
||||
// FilterPolicy (like NewBloomFilterPolicy) that does not ignore
|
||||
// trailing spaces in keys.
|
||||
LEVELDB_EXPORT const FilterPolicy* NewBloomFilterPolicy(int bits_per_key);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_FILTER_POLICY_H_
|
||||
30
3rdparty/leveldb/include/db/builder.h
vendored
Normal file
30
3rdparty/leveldb/include/db/builder.h
vendored
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
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_
|
||||
217
3rdparty/leveldb/include/db/db_impl.h
vendored
Normal file
217
3rdparty/leveldb/include/db/db_impl.h
vendored
Normal file
@@ -0,0 +1,217 @@
|
||||
// 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 <atomic>
|
||||
#include <deque>
|
||||
#include <set>
|
||||
#include <string>
|
||||
|
||||
#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"
|
||||
#include "port/thread_annotations.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);
|
||||
|
||||
DBImpl(const DBImpl&) = delete;
|
||||
DBImpl& operator=(const DBImpl&) = delete;
|
||||
|
||||
~DBImpl() override;
|
||||
|
||||
// Implementations of the DB interface
|
||||
Status Put(const WriteOptions&, const Slice& key,
|
||||
const Slice& value) override;
|
||||
Status Delete(const WriteOptions&, const Slice& key) override;
|
||||
Status Write(const WriteOptions& options, WriteBatch* updates) override;
|
||||
Status Get(const ReadOptions& options, const Slice& key,
|
||||
std::string* value) override;
|
||||
Iterator* NewIterator(const ReadOptions&) override;
|
||||
const Snapshot* GetSnapshot() override;
|
||||
void ReleaseSnapshot(const Snapshot* snapshot) override;
|
||||
bool GetProperty(const Slice& property, std::string* value) override;
|
||||
void GetApproximateSizes(const Range* range, int n, uint64_t* sizes) override;
|
||||
void CompactRange(const Slice* begin, const Slice* end) override;
|
||||
|
||||
// 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();
|
||||
|
||||
// Record a sample of bytes read at the specified internal key.
|
||||
// Samples are taken approximately once every config::kReadBytesPeriod
|
||||
// bytes.
|
||||
void RecordReadSample(Slice key);
|
||||
|
||||
private:
|
||||
friend class DB;
|
||||
struct CompactionState;
|
||||
struct Writer;
|
||||
|
||||
// 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
|
||||
};
|
||||
|
||||
// Per level compaction stats. stats_[level] stores the stats for
|
||||
// compactions that produced data for the specified "level".
|
||||
struct CompactionStats {
|
||||
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;
|
||||
}
|
||||
|
||||
int64_t micros;
|
||||
int64_t bytes_read;
|
||||
int64_t bytes_written;
|
||||
};
|
||||
|
||||
Iterator* NewInternalIterator(const ReadOptions&,
|
||||
SequenceNumber* latest_snapshot,
|
||||
uint32_t* seed);
|
||||
|
||||
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, bool* save_manifest)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
void MaybeIgnoreError(Status* s) const;
|
||||
|
||||
// Delete any unneeded files and stale in-memory entries.
|
||||
void RemoveObsoleteFiles() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Compact the in-memory write buffer to disk. Switches to a new
|
||||
// log-file/memtable and writes a new descriptor iff successful.
|
||||
// Errors are recorded in bg_error_.
|
||||
void CompactMemTable() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
Status RecoverLogFile(uint64_t log_number, bool last_log, bool* save_manifest,
|
||||
VersionEdit* edit, SequenceNumber* max_sequence)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
Status WriteLevel0Table(MemTable* mem, VersionEdit* edit, Version* base)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
Status MakeRoomForWrite(bool force /* compact even if there is room? */)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
WriteBatch* BuildBatchGroup(Writer** last_writer)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
void RecordBackgroundError(const Status& s);
|
||||
|
||||
void MaybeScheduleCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
static void BGWork(void* db);
|
||||
void BackgroundCall();
|
||||
void BackgroundCompaction() EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
void CleanupCompaction(CompactionState* compact)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
Status DoCompactionWork(CompactionState* compact)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
Status OpenCompactionOutputFile(CompactionState* compact);
|
||||
Status FinishCompactionOutputFile(CompactionState* compact, Iterator* input);
|
||||
Status InstallCompactionResults(CompactionState* compact)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
const Comparator* user_comparator() const {
|
||||
return internal_comparator_.user_comparator();
|
||||
}
|
||||
|
||||
// Constant after construction
|
||||
Env* const env_;
|
||||
const InternalKeyComparator internal_comparator_;
|
||||
const InternalFilterPolicy internal_filter_policy_;
|
||||
const Options options_; // options_.comparator == &internal_comparator_
|
||||
const bool owns_info_log_;
|
||||
const bool owns_cache_;
|
||||
const std::string dbname_;
|
||||
|
||||
// table_cache_ provides its own synchronization
|
||||
TableCache* const 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_;
|
||||
std::atomic<bool> shutting_down_;
|
||||
port::CondVar background_work_finished_signal_ GUARDED_BY(mutex_);
|
||||
MemTable* mem_;
|
||||
MemTable* imm_ GUARDED_BY(mutex_); // Memtable being compacted
|
||||
std::atomic<bool> has_imm_; // So bg thread can detect non-null imm_
|
||||
WritableFile* logfile_;
|
||||
uint64_t logfile_number_ GUARDED_BY(mutex_);
|
||||
log::Writer* log_;
|
||||
uint32_t seed_ GUARDED_BY(mutex_); // For sampling.
|
||||
|
||||
// Queue of writers.
|
||||
std::deque<Writer*> writers_ GUARDED_BY(mutex_);
|
||||
WriteBatch* tmp_batch_ GUARDED_BY(mutex_);
|
||||
|
||||
SnapshotList snapshots_ GUARDED_BY(mutex_);
|
||||
|
||||
// Set of table files to protect from deletion because they are
|
||||
// part of ongoing compactions.
|
||||
std::set<uint64_t> pending_outputs_ GUARDED_BY(mutex_);
|
||||
|
||||
// Has a background compaction been scheduled or is running?
|
||||
bool background_compaction_scheduled_ GUARDED_BY(mutex_);
|
||||
|
||||
ManualCompaction* manual_compaction_ GUARDED_BY(mutex_);
|
||||
|
||||
VersionSet* const versions_ GUARDED_BY(mutex_);
|
||||
|
||||
// Have we encountered a background error in paranoid mode?
|
||||
Status bg_error_ GUARDED_BY(mutex_);
|
||||
|
||||
CompactionStats stats_[config::kNumLevels] GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
// Sanitize db options. The caller should delete result.info_log if
|
||||
// it is not equal to src.info_log.
|
||||
Options SanitizeOptions(const std::string& db,
|
||||
const InternalKeyComparator* icmp,
|
||||
const InternalFilterPolicy* ipolicy,
|
||||
const Options& src);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_DB_IMPL_H_
|
||||
26
3rdparty/leveldb/include/db/db_iter.h
vendored
Normal file
26
3rdparty/leveldb/include/db/db_iter.h
vendored
Normal file
@@ -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 <cstdint>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "leveldb/db.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class DBImpl;
|
||||
|
||||
// Return a new iterator that converts internal keys (yielded by
|
||||
// "*internal_iter") that were live at the specified "sequence" number
|
||||
// into appropriate user keys.
|
||||
Iterator* NewDBIterator(DBImpl* db, const Comparator* user_key_comparator,
|
||||
Iterator* internal_iter, SequenceNumber sequence,
|
||||
uint32_t seed);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_DB_ITER_H_
|
||||
224
3rdparty/leveldb/include/db/dbformat.h
vendored
Normal file
224
3rdparty/leveldb/include/db/dbformat.h
vendored
Normal file
@@ -0,0 +1,224 @@
|
||||
// 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_DBFORMAT_H_
|
||||
#define STORAGE_LEVELDB_DB_DBFORMAT_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "leveldb/comparator.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "leveldb/filter_policy.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;
|
||||
|
||||
// Approximate gap in bytes between samples of data read during iteration.
|
||||
static const int kReadBytesPeriod = 1048576;
|
||||
|
||||
} // 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.
|
||||
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.
|
||||
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);
|
||||
}
|
||||
|
||||
// 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) {}
|
||||
const char* Name() const override;
|
||||
int Compare(const Slice& a, const Slice& b) const override;
|
||||
void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override;
|
||||
void FindShortSuccessor(std::string* key) const override;
|
||||
|
||||
const Comparator* user_comparator() const { return user_comparator_; }
|
||||
|
||||
int Compare(const InternalKey& a, const InternalKey& b) const;
|
||||
};
|
||||
|
||||
// Filter policy wrapper that converts from internal keys to user keys
|
||||
class InternalFilterPolicy : public FilterPolicy {
|
||||
private:
|
||||
const FilterPolicy* const user_policy_;
|
||||
|
||||
public:
|
||||
explicit InternalFilterPolicy(const FilterPolicy* p) : user_policy_(p) {}
|
||||
const char* Name() const override;
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const override;
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const override;
|
||||
};
|
||||
|
||||
// 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));
|
||||
}
|
||||
|
||||
bool DecodeFrom(const Slice& s) {
|
||||
rep_.assign(s.data(), s.size());
|
||||
return !rep_.empty();
|
||||
}
|
||||
|
||||
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);
|
||||
uint8_t 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<uint8_t>(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(const LookupKey&) = delete;
|
||||
LookupKey& operator=(const LookupKey&) = delete;
|
||||
|
||||
~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
|
||||
};
|
||||
|
||||
inline LookupKey::~LookupKey() {
|
||||
if (start_ != space_) delete[] start_;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_DBFORMAT_H_
|
||||
83
3rdparty/leveldb/include/db/filename.h
vendored
Normal file
83
3rdparty/leveldb/include/db/filename.h
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
// 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 <cstdint>
|
||||
#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".
|
||||
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".
|
||||
std::string TableFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
// Return the legacy file name for an sstable with the specified number
|
||||
// in the db named by "dbname". The result will be prefixed with
|
||||
// "dbname".
|
||||
std::string SSTTableFileName(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".
|
||||
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".
|
||||
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".
|
||||
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".
|
||||
std::string TempFileName(const std::string& dbname, uint64_t number);
|
||||
|
||||
// Return the name of the info log file for "dbname".
|
||||
std::string InfoLogFileName(const std::string& dbname);
|
||||
|
||||
// Return the name of the old info log file for "dbname".
|
||||
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.
|
||||
bool ParseFileName(const std::string& filename, uint64_t* number,
|
||||
FileType* type);
|
||||
|
||||
// Make the CURRENT file point to the descriptor file with the
|
||||
// specified number.
|
||||
Status SetCurrentFile(Env* env, const std::string& dbname,
|
||||
uint64_t descriptor_number);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_FILENAME_H_
|
||||
35
3rdparty/leveldb/include/db/log_format.h
vendored
Normal file
35
3rdparty/leveldb/include/db/log_format.h
vendored
Normal file
@@ -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.md 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), length (2 bytes), type (1 byte).
|
||||
static const int kHeaderSize = 4 + 2 + 1;
|
||||
|
||||
} // namespace log
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_LOG_FORMAT_H_
|
||||
112
3rdparty/leveldb/include/db/log_reader.h
vendored
Normal file
112
3rdparty/leveldb/include/db/log_reader.h
vendored
Normal file
@@ -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.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_DB_LOG_READER_H_
|
||||
#define STORAGE_LEVELDB_DB_LOG_READER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#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. "bytes" 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(const Reader&) = delete;
|
||||
Reader& operator=(const Reader&) = delete;
|
||||
|
||||
~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:
|
||||
// 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(uint64_t bytes, const char* reason);
|
||||
void ReportDrop(uint64_t bytes, const Status& reason);
|
||||
|
||||
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_;
|
||||
|
||||
// True if we are resynchronizing after a seek (initial_offset_ > 0). In
|
||||
// particular, a run of kMiddleType and kLastType records can be silently
|
||||
// skipped in this mode
|
||||
bool resyncing_;
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_LOG_READER_H_
|
||||
54
3rdparty/leveldb/include/db/log_writer.h
vendored
Normal file
54
3rdparty/leveldb/include/db/log_writer.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 <cstdint>
|
||||
|
||||
#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);
|
||||
|
||||
// Create a writer that will append data to "*dest".
|
||||
// "*dest" must have initial length "dest_length".
|
||||
// "*dest" must remain live while this Writer is in use.
|
||||
Writer(WritableFile* dest, uint64_t dest_length);
|
||||
|
||||
Writer(const Writer&) = delete;
|
||||
Writer& operator=(const Writer&) = delete;
|
||||
|
||||
~Writer();
|
||||
|
||||
Status AddRecord(const Slice& slice);
|
||||
|
||||
private:
|
||||
Status EmitPhysicalRecord(RecordType type, const char* ptr, size_t length);
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
} // namespace log
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_LOG_WRITER_H_
|
||||
87
3rdparty/leveldb/include/db/memtable.h
vendored
Normal file
87
3rdparty/leveldb/include/db/memtable.h
vendored
Normal file
@@ -0,0 +1,87 @@
|
||||
// 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 "db/dbformat.h"
|
||||
#include "db/skiplist.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "util/arena.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class InternalKeyComparator;
|
||||
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);
|
||||
|
||||
MemTable(const MemTable&) = delete;
|
||||
MemTable& operator=(const MemTable&) = delete;
|
||||
|
||||
// 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. It is safe to call when MemTable is being modified.
|
||||
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:
|
||||
friend class MemTableIterator;
|
||||
friend class MemTableBackwardIterator;
|
||||
|
||||
struct KeyComparator {
|
||||
const InternalKeyComparator comparator;
|
||||
explicit KeyComparator(const InternalKeyComparator& c) : comparator(c) {}
|
||||
int operator()(const char* a, const char* b) const;
|
||||
};
|
||||
|
||||
typedef SkipList<const char*, KeyComparator> Table;
|
||||
|
||||
~MemTable(); // Private since only Unref() should be used to delete it
|
||||
|
||||
KeyComparator comparator_;
|
||||
int refs_;
|
||||
Arena arena_;
|
||||
Table table_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_MEMTABLE_H_
|
||||
380
3rdparty/leveldb/include/db/skiplist.h
vendored
Normal file
380
3rdparty/leveldb/include/db/skiplist.h
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
// 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_SKIPLIST_H_
|
||||
#define STORAGE_LEVELDB_DB_SKIPLIST_H_
|
||||
|
||||
// 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 <atomic>
|
||||
#include <cassert>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "util/arena.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
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);
|
||||
|
||||
SkipList(const SkipList&) = delete;
|
||||
SkipList& operator=(const SkipList&) = delete;
|
||||
|
||||
// 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 };
|
||||
|
||||
inline int GetMaxHeight() const {
|
||||
return max_height_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
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 nullptr 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;
|
||||
|
||||
// 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.
|
||||
std::atomic<int> max_height_; // Height of the entire list
|
||||
|
||||
// Read/written only by Insert().
|
||||
Random rnd_;
|
||||
};
|
||||
|
||||
// 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 next_[n].load(std::memory_order_acquire);
|
||||
}
|
||||
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].store(x, std::memory_order_release);
|
||||
}
|
||||
|
||||
// No-barrier variants that can be safely used in a few locations.
|
||||
Node* NoBarrier_Next(int n) {
|
||||
assert(n >= 0);
|
||||
return next_[n].load(std::memory_order_relaxed);
|
||||
}
|
||||
void NoBarrier_SetNext(int n, Node* x) {
|
||||
assert(n >= 0);
|
||||
next_[n].store(x, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
private:
|
||||
// Array of length equal to the node height. next_[0] is lowest level link.
|
||||
std::atomic<Node*> next_[1];
|
||||
};
|
||||
|
||||
template <typename Key, class Comparator>
|
||||
typename SkipList<Key, Comparator>::Node* SkipList<Key, Comparator>::NewNode(
|
||||
const Key& key, int height) {
|
||||
char* const node_memory = arena_->AllocateAligned(
|
||||
sizeof(Node) + sizeof(std::atomic<Node*>) * (height - 1));
|
||||
return new (node_memory) Node(key);
|
||||
}
|
||||
|
||||
template <typename Key, class Comparator>
|
||||
inline SkipList<Key, Comparator>::Iterator::Iterator(const SkipList* list) {
|
||||
list_ = list;
|
||||
node_ = nullptr;
|
||||
}
|
||||
|
||||
template <typename Key, class Comparator>
|
||||
inline bool SkipList<Key, Comparator>::Iterator::Valid() const {
|
||||
return node_ != nullptr;
|
||||
}
|
||||
|
||||
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_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename Key, class Comparator>
|
||||
inline void SkipList<Key, Comparator>::Iterator::Seek(const Key& target) {
|
||||
node_ = list_->FindGreaterOrEqual(target, nullptr);
|
||||
}
|
||||
|
||||
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_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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_.OneIn(kBranching)) {
|
||||
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 != nullptr) && (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 != nullptr) 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 == nullptr || 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 == nullptr) {
|
||||
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_(1),
|
||||
rnd_(0xdeadbeef) {
|
||||
for (int i = 0; i < kMaxHeight; i++) {
|
||||
head_->SetNext(i, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
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 == nullptr || !Equal(key, x->key));
|
||||
|
||||
int height = RandomHeight();
|
||||
if (height > GetMaxHeight()) {
|
||||
for (int i = GetMaxHeight(); i < height; i++) {
|
||||
prev[i] = head_;
|
||||
}
|
||||
// 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_ (nullptr), or a new value set in
|
||||
// the loop below. In the former case the reader will
|
||||
// immediately drop to the next level since nullptr sorts after all
|
||||
// keys. In the latter case the reader will use the new node.
|
||||
max_height_.store(height, std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
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, nullptr);
|
||||
if (x != nullptr && Equal(key, x->key)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_SKIPLIST_H_
|
||||
95
3rdparty/leveldb/include/db/snapshot.h
vendored
Normal file
95
3rdparty/leveldb/include/db/snapshot.h
vendored
Normal file
@@ -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.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_DB_SNAPSHOT_H_
|
||||
#define STORAGE_LEVELDB_DB_SNAPSHOT_H_
|
||||
|
||||
#include "db/dbformat.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:
|
||||
SnapshotImpl(SequenceNumber sequence_number)
|
||||
: sequence_number_(sequence_number) {}
|
||||
|
||||
SequenceNumber sequence_number() const { return sequence_number_; }
|
||||
|
||||
private:
|
||||
friend class SnapshotList;
|
||||
|
||||
// SnapshotImpl is kept in a doubly-linked circular list. The SnapshotList
|
||||
// implementation operates on the next/previous fields directly.
|
||||
SnapshotImpl* prev_;
|
||||
SnapshotImpl* next_;
|
||||
|
||||
const SequenceNumber sequence_number_;
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
SnapshotList* list_ = nullptr;
|
||||
#endif // !defined(NDEBUG)
|
||||
};
|
||||
|
||||
class SnapshotList {
|
||||
public:
|
||||
SnapshotList() : head_(0) {
|
||||
head_.prev_ = &head_;
|
||||
head_.next_ = &head_;
|
||||
}
|
||||
|
||||
bool empty() const { return head_.next_ == &head_; }
|
||||
SnapshotImpl* oldest() const {
|
||||
assert(!empty());
|
||||
return head_.next_;
|
||||
}
|
||||
SnapshotImpl* newest() const {
|
||||
assert(!empty());
|
||||
return head_.prev_;
|
||||
}
|
||||
|
||||
// Creates a SnapshotImpl and appends it to the end of the list.
|
||||
SnapshotImpl* New(SequenceNumber sequence_number) {
|
||||
assert(empty() || newest()->sequence_number_ <= sequence_number);
|
||||
|
||||
SnapshotImpl* snapshot = new SnapshotImpl(sequence_number);
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
snapshot->list_ = this;
|
||||
#endif // !defined(NDEBUG)
|
||||
snapshot->next_ = &head_;
|
||||
snapshot->prev_ = head_.prev_;
|
||||
snapshot->prev_->next_ = snapshot;
|
||||
snapshot->next_->prev_ = snapshot;
|
||||
return snapshot;
|
||||
}
|
||||
|
||||
// Removes a SnapshotImpl from this list.
|
||||
//
|
||||
// The snapshot must have been created by calling New() on this list.
|
||||
//
|
||||
// The snapshot pointer should not be const, because its memory is
|
||||
// deallocated. However, that would force us to change DB::ReleaseSnapshot(),
|
||||
// which is in the API, and currently takes a const Snapshot.
|
||||
void Delete(const SnapshotImpl* snapshot) {
|
||||
#if !defined(NDEBUG)
|
||||
assert(snapshot->list_ == this);
|
||||
#endif // !defined(NDEBUG)
|
||||
snapshot->prev_->next_ = snapshot->next_;
|
||||
snapshot->next_->prev_ = snapshot->prev_;
|
||||
delete snapshot;
|
||||
}
|
||||
|
||||
private:
|
||||
// Dummy head of doubly-linked list of snapshots
|
||||
SnapshotImpl head_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_SNAPSHOT_H_
|
||||
61
3rdparty/leveldb/include/db/table_cache.h
vendored
Normal file
61
3rdparty/leveldb/include/db/table_cache.h
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
// 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 <cstdint>
|
||||
#include <string>
|
||||
|
||||
#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(const TableCache&) = delete;
|
||||
TableCache& operator=(const TableCache&) = delete;
|
||||
|
||||
~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 to nullptr 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 = nullptr);
|
||||
|
||||
// If a seek to internal key "k" in specified file finds an entry,
|
||||
// call (*handle_result)(arg, found_key, found_value).
|
||||
Status Get(const ReadOptions& options, uint64_t file_number,
|
||||
uint64_t file_size, const Slice& k, void* arg,
|
||||
void (*handle_result)(void*, const Slice&, const Slice&));
|
||||
|
||||
// Evict any entry for the specified file number
|
||||
void Evict(uint64_t file_number);
|
||||
|
||||
private:
|
||||
Status FindTable(uint64_t file_number, uint64_t file_size, Cache::Handle**);
|
||||
|
||||
Env* const env_;
|
||||
const std::string dbname_;
|
||||
const Options& options_;
|
||||
Cache* cache_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_TABLE_CACHE_H_
|
||||
106
3rdparty/leveldb/include/db/version_edit.h
vendored
Normal file
106
3rdparty/leveldb/include/db/version_edit.h
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// 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 {
|
||||
FileMetaData() : refs(0), allowed_seeks(1 << 30), file_size(0) {}
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
class VersionEdit {
|
||||
public:
|
||||
VersionEdit() { Clear(); }
|
||||
~VersionEdit() = default;
|
||||
|
||||
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 RemoveFile(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_
|
||||
393
3rdparty/leveldb/include/db/version_set.h
vendored
Normal file
393
3rdparty/leveldb/include/db/version_set.h
vendored
Normal file
@@ -0,0 +1,393 @@
|
||||
// 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"
|
||||
#include "port/thread_annotations.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.
|
||||
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==nullptr represents a key smaller than all keys in the DB.
|
||||
// largest==nullptr represents a key largest than all keys in the DB.
|
||||
// REQUIRES: If disjoint_sorted_files, files[] contains disjoint ranges
|
||||
// in sorted order.
|
||||
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:
|
||||
struct GetStats {
|
||||
FileMetaData* seek_file;
|
||||
int seek_file_level;
|
||||
};
|
||||
|
||||
// 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
|
||||
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);
|
||||
|
||||
// Record a sample of bytes read at the specified internal key.
|
||||
// Samples are taken approximately once every config::kReadBytesPeriod
|
||||
// bytes. Returns true if a new compaction may need to be triggered.
|
||||
// REQUIRES: lock is held
|
||||
bool RecordReadSample(Slice key);
|
||||
|
||||
// Reference count management (so Versions do not disappear out from
|
||||
// under live iterators)
|
||||
void Ref();
|
||||
void Unref();
|
||||
|
||||
void GetOverlappingInputs(
|
||||
int level,
|
||||
const InternalKey* begin, // nullptr means before all keys
|
||||
const InternalKey* end, // nullptr 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==nullptr represents a key smaller than all the DB's keys.
|
||||
// largest_user_key==nullptr represents a key largest than all the DB's keys.
|
||||
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;
|
||||
|
||||
explicit Version(VersionSet* vset)
|
||||
: vset_(vset),
|
||||
next_(this),
|
||||
prev_(this),
|
||||
refs_(0),
|
||||
file_to_compact_(nullptr),
|
||||
file_to_compact_level_(-1),
|
||||
compaction_score_(-1),
|
||||
compaction_level_(-1) {}
|
||||
|
||||
Version(const Version&) = delete;
|
||||
Version& operator=(const Version&) = delete;
|
||||
|
||||
~Version();
|
||||
|
||||
Iterator* NewConcatenatingIterator(const ReadOptions&, int level) const;
|
||||
|
||||
// Call func(arg, level, f) for every file that overlaps user_key in
|
||||
// order from newest to oldest. If an invocation of func returns
|
||||
// false, makes no more calls.
|
||||
//
|
||||
// REQUIRES: user portion of internal_key == user_key.
|
||||
void ForEachOverlapping(Slice user_key, Slice internal_key, void* arg,
|
||||
bool (*func)(void*, int, FileMetaData*));
|
||||
|
||||
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_;
|
||||
};
|
||||
|
||||
class VersionSet {
|
||||
public:
|
||||
VersionSet(const std::string& dbname, const Options* options,
|
||||
TableCache* table_cache, const InternalKeyComparator*);
|
||||
VersionSet(const VersionSet&) = delete;
|
||||
VersionSet& operator=(const VersionSet&) = delete;
|
||||
|
||||
~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)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mu);
|
||||
|
||||
// Recover the last saved descriptor from persistent storage.
|
||||
Status Recover(bool* save_manifest);
|
||||
|
||||
// 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_++; }
|
||||
|
||||
// Arrange to reuse "file_number" unless a newer file number has
|
||||
// already been allocated.
|
||||
// REQUIRES: "file_number" was returned by a call to NewFileNumber().
|
||||
void ReuseFileNumber(uint64_t file_number) {
|
||||
if (next_file_number_ == file_number + 1) {
|
||||
next_file_number_ = 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 nullptr 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 nullptr 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_ != nullptr);
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
bool ReuseManifest(const std::string& dscname, const std::string& dscbase);
|
||||
|
||||
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];
|
||||
};
|
||||
|
||||
// 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;
|
||||
|
||||
Compaction(const Options* options, 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 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_
|
||||
45
3rdparty/leveldb/include/db/write_batch_internal.h
vendored
Normal file
45
3rdparty/leveldb/include/db/write_batch_internal.h
vendored
Normal file
@@ -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_DB_WRITE_BATCH_INTERNAL_H_
|
||||
#define STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_
|
||||
|
||||
#include "db/dbformat.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 sequence number for the start of this batch.
|
||||
static SequenceNumber Sequence(const WriteBatch* batch);
|
||||
|
||||
// Store the specified number as the sequence 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);
|
||||
|
||||
static void Append(WriteBatch* dst, const WriteBatch* src);
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_DB_WRITE_BATCH_INTERNAL_H_
|
||||
390
3rdparty/leveldb/include/helpers/memenv/memenv.cc
vendored
Normal file
390
3rdparty/leveldb/include/helpers/memenv/memenv.cc
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
// 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 <cstring>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/status.h"
|
||||
#include "port/port.h"
|
||||
#include "port/thread_annotations.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
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) {}
|
||||
|
||||
// No copying allowed.
|
||||
FileState(const FileState&) = delete;
|
||||
FileState& operator=(const FileState&) = delete;
|
||||
|
||||
// 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 {
|
||||
MutexLock lock(&blocks_mutex_);
|
||||
return size_;
|
||||
}
|
||||
|
||||
void Truncate() {
|
||||
MutexLock lock(&blocks_mutex_);
|
||||
for (char*& block : blocks_) {
|
||||
delete[] block;
|
||||
}
|
||||
blocks_.clear();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {
|
||||
MutexLock lock(&blocks_mutex_);
|
||||
if (offset > size_) {
|
||||
return Status::IOError("Offset greater than file size.");
|
||||
}
|
||||
const uint64_t available = size_ - offset;
|
||||
if (n > available) {
|
||||
n = static_cast<size_t>(available);
|
||||
}
|
||||
if (n == 0) {
|
||||
*result = Slice();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
assert(offset / kBlockSize <= std::numeric_limits<size_t>::max());
|
||||
size_t block = static_cast<size_t>(offset / kBlockSize);
|
||||
size_t block_offset = offset % kBlockSize;
|
||||
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;
|
||||
}
|
||||
std::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();
|
||||
|
||||
MutexLock lock(&blocks_mutex_);
|
||||
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;
|
||||
}
|
||||
std::memcpy(blocks_.back() + offset, src, avail);
|
||||
src_len -= avail;
|
||||
src += avail;
|
||||
size_ += avail;
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
enum { kBlockSize = 8 * 1024 };
|
||||
|
||||
// Private since only Unref() should be used to delete it.
|
||||
~FileState() { Truncate(); }
|
||||
|
||||
port::Mutex refs_mutex_;
|
||||
int refs_ GUARDED_BY(refs_mutex_);
|
||||
|
||||
mutable port::Mutex blocks_mutex_;
|
||||
std::vector<char*> blocks_ GUARDED_BY(blocks_mutex_);
|
||||
uint64_t size_ GUARDED_BY(blocks_mutex_);
|
||||
};
|
||||
|
||||
class SequentialFileImpl : public SequentialFile {
|
||||
public:
|
||||
explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) {
|
||||
file_->Ref();
|
||||
}
|
||||
|
||||
~SequentialFileImpl() override { file_->Unref(); }
|
||||
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
Status s = file_->Read(pos_, n, result, scratch);
|
||||
if (s.ok()) {
|
||||
pos_ += result->size();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status Skip(uint64_t n) override {
|
||||
if (pos_ > file_->Size()) {
|
||||
return Status::IOError("pos_ > file_->Size()");
|
||||
}
|
||||
const uint64_t available = file_->Size() - pos_;
|
||||
if (n > available) {
|
||||
n = available;
|
||||
}
|
||||
pos_ += n;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
FileState* file_;
|
||||
uint64_t pos_;
|
||||
};
|
||||
|
||||
class RandomAccessFileImpl : public RandomAccessFile {
|
||||
public:
|
||||
explicit RandomAccessFileImpl(FileState* file) : file_(file) { file_->Ref(); }
|
||||
|
||||
~RandomAccessFileImpl() override { file_->Unref(); }
|
||||
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
return file_->Read(offset, n, result, scratch);
|
||||
}
|
||||
|
||||
private:
|
||||
FileState* file_;
|
||||
};
|
||||
|
||||
class WritableFileImpl : public WritableFile {
|
||||
public:
|
||||
WritableFileImpl(FileState* file) : file_(file) { file_->Ref(); }
|
||||
|
||||
~WritableFileImpl() override { file_->Unref(); }
|
||||
|
||||
Status Append(const Slice& data) override { return file_->Append(data); }
|
||||
|
||||
Status Close() override { return Status::OK(); }
|
||||
Status Flush() override { return Status::OK(); }
|
||||
Status Sync() override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
FileState* file_;
|
||||
};
|
||||
|
||||
class NoOpLogger : public Logger {
|
||||
public:
|
||||
void Logv(const char* format, std::va_list ap) override {}
|
||||
};
|
||||
|
||||
class InMemoryEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) {}
|
||||
|
||||
~InMemoryEnv() override {
|
||||
for (const auto& kvp : file_map_) {
|
||||
kvp.second->Unref();
|
||||
}
|
||||
}
|
||||
|
||||
// Partial implementation of the Env interface.
|
||||
Status NewSequentialFile(const std::string& fname,
|
||||
SequentialFile** result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(fname) == file_map_.end()) {
|
||||
*result = nullptr;
|
||||
return Status::IOError(fname, "File not found");
|
||||
}
|
||||
|
||||
*result = new SequentialFileImpl(file_map_[fname]);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewRandomAccessFile(const std::string& fname,
|
||||
RandomAccessFile** result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(fname) == file_map_.end()) {
|
||||
*result = nullptr;
|
||||
return Status::IOError(fname, "File not found");
|
||||
}
|
||||
|
||||
*result = new RandomAccessFileImpl(file_map_[fname]);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewWritableFile(const std::string& fname,
|
||||
WritableFile** result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
FileSystem::iterator it = file_map_.find(fname);
|
||||
|
||||
FileState* file;
|
||||
if (it == file_map_.end()) {
|
||||
// File is not currently open.
|
||||
file = new FileState();
|
||||
file->Ref();
|
||||
file_map_[fname] = file;
|
||||
} else {
|
||||
file = it->second;
|
||||
file->Truncate();
|
||||
}
|
||||
|
||||
*result = new WritableFileImpl(file);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewAppendableFile(const std::string& fname,
|
||||
WritableFile** result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
FileState** sptr = &file_map_[fname];
|
||||
FileState* file = *sptr;
|
||||
if (file == nullptr) {
|
||||
file = new FileState();
|
||||
file->Ref();
|
||||
}
|
||||
*result = new WritableFileImpl(file);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool FileExists(const std::string& fname) override {
|
||||
MutexLock lock(&mutex_);
|
||||
return file_map_.find(fname) != file_map_.end();
|
||||
}
|
||||
|
||||
Status GetChildren(const std::string& dir,
|
||||
std::vector<std::string>* result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
result->clear();
|
||||
|
||||
for (const auto& kvp : file_map_) {
|
||||
const std::string& filename = kvp.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 RemoveFileInternal(const std::string& fname)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
|
||||
if (file_map_.find(fname) == file_map_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
file_map_[fname]->Unref();
|
||||
file_map_.erase(fname);
|
||||
}
|
||||
|
||||
Status RemoveFile(const std::string& fname) override {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(fname) == file_map_.end()) {
|
||||
return Status::IOError(fname, "File not found");
|
||||
}
|
||||
|
||||
RemoveFileInternal(fname);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CreateDir(const std::string& dirname) override { return Status::OK(); }
|
||||
|
||||
Status RemoveDir(const std::string& dirname) override { return Status::OK(); }
|
||||
|
||||
Status GetFileSize(const std::string& fname, uint64_t* file_size) override {
|
||||
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();
|
||||
}
|
||||
|
||||
Status RenameFile(const std::string& src,
|
||||
const std::string& target) override {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(src) == file_map_.end()) {
|
||||
return Status::IOError(src, "File not found");
|
||||
}
|
||||
|
||||
RemoveFileInternal(target);
|
||||
file_map_[target] = file_map_[src];
|
||||
file_map_.erase(src);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status LockFile(const std::string& fname, FileLock** lock) override {
|
||||
*lock = new FileLock;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status UnlockFile(FileLock* lock) override {
|
||||
delete lock;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetTestDirectory(std::string* path) override {
|
||||
*path = "/test";
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewLogger(const std::string& fname, Logger** result) override {
|
||||
*result = new NoOpLogger;
|
||||
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_ GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Env* NewMemEnv(Env* base_env) { return new InMemoryEnv(base_env); }
|
||||
|
||||
} // namespace leveldb
|
||||
22
3rdparty/leveldb/include/helpers/memenv/memenv.h
vendored
Normal file
22
3rdparty/leveldb/include/helpers/memenv/memenv.h
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// 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_
|
||||
|
||||
#include "leveldb/export.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.
|
||||
LEVELDB_EXPORT Env* NewMemEnv(Env* base_env);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_HELPERS_MEMENV_MEMENV_H_
|
||||
259
3rdparty/leveldb/include/helpers/memenv/memenv_test.cc
vendored
Normal file
259
3rdparty/leveldb/include/helpers/memenv/memenv_test.cc
vendored
Normal file
@@ -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 "helpers/memenv/memenv.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "db/db_impl.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "util/testutil.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class MemEnvTest : public testing::Test {
|
||||
public:
|
||||
MemEnvTest() : env_(NewMemEnv(Env::Default())) {}
|
||||
~MemEnvTest() { delete env_; }
|
||||
|
||||
Env* env_;
|
||||
};
|
||||
|
||||
TEST_F(MemEnvTest, Basics) {
|
||||
uint64_t file_size;
|
||||
WritableFile* writable_file;
|
||||
std::vector<std::string> children;
|
||||
|
||||
ASSERT_LEVELDB_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_LEVELDB_OK(env_->GetChildren("/dir", &children));
|
||||
ASSERT_EQ(0, children.size());
|
||||
|
||||
// Create a file.
|
||||
ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f", &writable_file));
|
||||
ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f", &file_size));
|
||||
ASSERT_EQ(0, file_size);
|
||||
delete writable_file;
|
||||
|
||||
// Check that the file exists.
|
||||
ASSERT_TRUE(env_->FileExists("/dir/f"));
|
||||
ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f", &file_size));
|
||||
ASSERT_EQ(0, file_size);
|
||||
ASSERT_LEVELDB_OK(env_->GetChildren("/dir", &children));
|
||||
ASSERT_EQ(1, children.size());
|
||||
ASSERT_EQ("f", children[0]);
|
||||
|
||||
// Write to the file.
|
||||
ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f", &writable_file));
|
||||
ASSERT_LEVELDB_OK(writable_file->Append("abc"));
|
||||
delete writable_file;
|
||||
|
||||
// Check that append works.
|
||||
ASSERT_LEVELDB_OK(env_->NewAppendableFile("/dir/f", &writable_file));
|
||||
ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f", &file_size));
|
||||
ASSERT_EQ(3, file_size);
|
||||
ASSERT_LEVELDB_OK(writable_file->Append("hello"));
|
||||
delete writable_file;
|
||||
|
||||
// Check for expected size.
|
||||
ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/f", &file_size));
|
||||
ASSERT_EQ(8, file_size);
|
||||
|
||||
// Check that renaming works.
|
||||
ASSERT_TRUE(!env_->RenameFile("/dir/non_existent", "/dir/g").ok());
|
||||
ASSERT_LEVELDB_OK(env_->RenameFile("/dir/f", "/dir/g"));
|
||||
ASSERT_TRUE(!env_->FileExists("/dir/f"));
|
||||
ASSERT_TRUE(env_->FileExists("/dir/g"));
|
||||
ASSERT_LEVELDB_OK(env_->GetFileSize("/dir/g", &file_size));
|
||||
ASSERT_EQ(8, 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_->RemoveFile("/dir/non_existent").ok());
|
||||
ASSERT_LEVELDB_OK(env_->RemoveFile("/dir/g"));
|
||||
ASSERT_TRUE(!env_->FileExists("/dir/g"));
|
||||
ASSERT_LEVELDB_OK(env_->GetChildren("/dir", &children));
|
||||
ASSERT_EQ(0, children.size());
|
||||
ASSERT_LEVELDB_OK(env_->RemoveDir("/dir"));
|
||||
}
|
||||
|
||||
TEST_F(MemEnvTest, ReadWrite) {
|
||||
WritableFile* writable_file;
|
||||
SequentialFile* seq_file;
|
||||
RandomAccessFile* rand_file;
|
||||
Slice result;
|
||||
char scratch[100];
|
||||
|
||||
ASSERT_LEVELDB_OK(env_->CreateDir("/dir"));
|
||||
|
||||
ASSERT_LEVELDB_OK(env_->NewWritableFile("/dir/f", &writable_file));
|
||||
ASSERT_LEVELDB_OK(writable_file->Append("hello "));
|
||||
ASSERT_LEVELDB_OK(writable_file->Append("world"));
|
||||
delete writable_file;
|
||||
|
||||
// Read sequentially.
|
||||
ASSERT_LEVELDB_OK(env_->NewSequentialFile("/dir/f", &seq_file));
|
||||
ASSERT_LEVELDB_OK(seq_file->Read(5, &result, scratch)); // Read "hello".
|
||||
ASSERT_EQ(0, result.compare("hello"));
|
||||
ASSERT_LEVELDB_OK(seq_file->Skip(1));
|
||||
ASSERT_LEVELDB_OK(seq_file->Read(1000, &result, scratch)); // Read "world".
|
||||
ASSERT_EQ(0, result.compare("world"));
|
||||
ASSERT_LEVELDB_OK(
|
||||
seq_file->Read(1000, &result, scratch)); // Try reading past EOF.
|
||||
ASSERT_EQ(0, result.size());
|
||||
ASSERT_LEVELDB_OK(seq_file->Skip(100)); // Try to skip past end of file.
|
||||
ASSERT_LEVELDB_OK(seq_file->Read(1000, &result, scratch));
|
||||
ASSERT_EQ(0, result.size());
|
||||
delete seq_file;
|
||||
|
||||
// Random reads.
|
||||
ASSERT_LEVELDB_OK(env_->NewRandomAccessFile("/dir/f", &rand_file));
|
||||
ASSERT_LEVELDB_OK(rand_file->Read(6, 5, &result, scratch)); // Read "world".
|
||||
ASSERT_EQ(0, result.compare("world"));
|
||||
ASSERT_LEVELDB_OK(rand_file->Read(0, 5, &result, scratch)); // Read "hello".
|
||||
ASSERT_EQ(0, result.compare("hello"));
|
||||
ASSERT_LEVELDB_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_F(MemEnvTest, Locks) {
|
||||
FileLock* lock;
|
||||
|
||||
// These are no-ops, but we test they return success.
|
||||
ASSERT_LEVELDB_OK(env_->LockFile("some file", &lock));
|
||||
ASSERT_LEVELDB_OK(env_->UnlockFile(lock));
|
||||
}
|
||||
|
||||
TEST_F(MemEnvTest, Misc) {
|
||||
std::string test_dir;
|
||||
ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
|
||||
ASSERT_TRUE(!test_dir.empty());
|
||||
|
||||
WritableFile* writable_file;
|
||||
ASSERT_LEVELDB_OK(env_->NewWritableFile("/a/b", &writable_file));
|
||||
|
||||
// These are no-ops, but we test they return success.
|
||||
ASSERT_LEVELDB_OK(writable_file->Sync());
|
||||
ASSERT_LEVELDB_OK(writable_file->Flush());
|
||||
ASSERT_LEVELDB_OK(writable_file->Close());
|
||||
delete writable_file;
|
||||
}
|
||||
|
||||
TEST_F(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_LEVELDB_OK(env_->NewWritableFile("/dir/f", &writable_file));
|
||||
ASSERT_LEVELDB_OK(writable_file->Append("foo"));
|
||||
ASSERT_LEVELDB_OK(writable_file->Append(write_data));
|
||||
delete writable_file;
|
||||
|
||||
SequentialFile* seq_file;
|
||||
Slice result;
|
||||
ASSERT_LEVELDB_OK(env_->NewSequentialFile("/dir/f", &seq_file));
|
||||
ASSERT_LEVELDB_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_LEVELDB_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_F(MemEnvTest, OverwriteOpenFile) {
|
||||
const char kWrite1Data[] = "Write #1 data";
|
||||
const size_t kFileDataLen = sizeof(kWrite1Data) - 1;
|
||||
const std::string kTestFileName = testing::TempDir() + "leveldb-TestFile.dat";
|
||||
|
||||
ASSERT_LEVELDB_OK(WriteStringToFile(env_, kWrite1Data, kTestFileName));
|
||||
|
||||
RandomAccessFile* rand_file;
|
||||
ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(kTestFileName, &rand_file));
|
||||
|
||||
const char kWrite2Data[] = "Write #2 data";
|
||||
ASSERT_LEVELDB_OK(WriteStringToFile(env_, kWrite2Data, kTestFileName));
|
||||
|
||||
// Verify that overwriting an open file will result in the new file data
|
||||
// being read from files opened before the write.
|
||||
Slice result;
|
||||
char scratch[kFileDataLen];
|
||||
ASSERT_LEVELDB_OK(rand_file->Read(0, kFileDataLen, &result, scratch));
|
||||
ASSERT_EQ(0, result.compare(kWrite2Data));
|
||||
|
||||
delete rand_file;
|
||||
}
|
||||
|
||||
TEST_F(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_LEVELDB_OK(DB::Open(options, "/dir/db", &db));
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
ASSERT_LEVELDB_OK(db->Put(WriteOptions(), keys[i], vals[i]));
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
std::string res;
|
||||
ASSERT_LEVELDB_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_LEVELDB_OK(dbi->TEST_CompactMemTable());
|
||||
|
||||
for (size_t i = 0; i < 3; ++i) {
|
||||
std::string res;
|
||||
ASSERT_LEVELDB_OK(db->Get(ReadOptions(), keys[i], &res));
|
||||
ASSERT_TRUE(res == vals[i]);
|
||||
}
|
||||
|
||||
delete db;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
10
3rdparty/leveldb/include/port/README.md
vendored
Normal file
10
3rdparty/leveldb/include/port/README.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
This directory contains interfaces and implementations that isolate the
|
||||
rest of the package from platform details.
|
||||
|
||||
Code in the rest of the package includes "port.h" from this directory.
|
||||
"port.h" in turn includes a platform specific "port_<platform>.h" file
|
||||
that provides the platform specific implementation.
|
||||
|
||||
See port_stdcxx.h for an example of what must be provided in a platform
|
||||
specific header file.
|
||||
|
||||
19
3rdparty/leveldb/include/port/port.h
vendored
Normal file
19
3rdparty/leveldb/include/port/port.h
vendored
Normal file
@@ -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.
|
||||
|
||||
#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) || defined(LEVELDB_PLATFORM_WINDOWS)
|
||||
#include "port/port_stdcxx.h"
|
||||
#elif defined(LEVELDB_PLATFORM_CHROMIUM)
|
||||
#include "port/port_chromium.h"
|
||||
#endif
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_H_
|
||||
38
3rdparty/leveldb/include/port/port_config.h.in
vendored
Normal file
38
3rdparty/leveldb/include/port/port_config.h.in
vendored
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2017 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_CONFIG_H_
|
||||
#define STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
|
||||
|
||||
// Define to 1 if you have a definition for fdatasync() in <unistd.h>.
|
||||
#if !defined(HAVE_FDATASYNC)
|
||||
#cmakedefine01 HAVE_FDATASYNC
|
||||
#endif // !defined(HAVE_FDATASYNC)
|
||||
|
||||
// Define to 1 if you have a definition for F_FULLFSYNC in <fcntl.h>.
|
||||
#if !defined(HAVE_FULLFSYNC)
|
||||
#cmakedefine01 HAVE_FULLFSYNC
|
||||
#endif // !defined(HAVE_FULLFSYNC)
|
||||
|
||||
// Define to 1 if you have a definition for O_CLOEXEC in <fcntl.h>.
|
||||
#if !defined(HAVE_O_CLOEXEC)
|
||||
#cmakedefine01 HAVE_O_CLOEXEC
|
||||
#endif // !defined(HAVE_O_CLOEXEC)
|
||||
|
||||
// Define to 1 if you have Google CRC32C.
|
||||
#if !defined(HAVE_CRC32C)
|
||||
#cmakedefine01 HAVE_CRC32C
|
||||
#endif // !defined(HAVE_CRC32C)
|
||||
|
||||
// Define to 1 if you have Google Snappy.
|
||||
#if !defined(HAVE_SNAPPY)
|
||||
#cmakedefine01 HAVE_SNAPPY
|
||||
#endif // !defined(HAVE_SNAPPY)
|
||||
|
||||
// Define to 1 if you have Zstd.
|
||||
#if !defined(HAVE_Zstd)
|
||||
#cmakedefine01 HAVE_ZSTD
|
||||
#endif // !defined(HAVE_ZSTD)
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_CONFIG_H_
|
||||
120
3rdparty/leveldb/include/port/port_example.h
vendored
Normal file
120
3rdparty/leveldb/include/port/port_example.h
vendored
Normal file
@@ -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.
|
||||
//
|
||||
// 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_
|
||||
|
||||
#include "port/thread_annotations.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.
|
||||
|
||||
// ------------------ Threading -------------------
|
||||
|
||||
// A Mutex represents an exclusive lock.
|
||||
class LOCKABLE 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() EXCLUSIVE_LOCK_FUNCTION();
|
||||
|
||||
// Unlock the mutex.
|
||||
// REQUIRES: This mutex was locked by this thread.
|
||||
void Unlock() UNLOCK_FUNCTION();
|
||||
|
||||
// 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() ASSERT_EXCLUSIVE_LOCK();
|
||||
};
|
||||
|
||||
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 SignalAll();
|
||||
};
|
||||
|
||||
// ------------------ Compression -------------------
|
||||
|
||||
// Store the snappy compression of "input[0,input_length-1]" in *output.
|
||||
// Returns false if snappy is not supported by this port.
|
||||
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.
|
||||
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 snappy
|
||||
// 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.
|
||||
bool Snappy_Uncompress(const char* input_data, size_t input_length,
|
||||
char* output);
|
||||
|
||||
// Store the zstd compression of "input[0,input_length-1]" in *output.
|
||||
// Returns false if zstd is not supported by this port.
|
||||
bool Zstd_Compress(int level, const char* input, size_t input_length,
|
||||
std::string* output);
|
||||
|
||||
// If input[0,input_length-1] looks like a valid zstd compressed
|
||||
// buffer, store the size of the uncompressed data in *result and
|
||||
// return true. Else return false.
|
||||
bool Zstd_GetUncompressedLength(const char* input, size_t length,
|
||||
size_t* result);
|
||||
|
||||
// Attempt to zstd uncompress input[0,input_length-1] into *output.
|
||||
// Returns true if successful, false if the input is invalid zstd
|
||||
// compressed data.
|
||||
//
|
||||
// REQUIRES: at least the first "n" bytes of output[] must be writable
|
||||
// where "n" is the result of a successful call to
|
||||
// Zstd_GetUncompressedLength.
|
||||
bool Zstd_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.
|
||||
bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg);
|
||||
|
||||
// Extend the CRC to include the first n bytes of buf.
|
||||
//
|
||||
// Returns zero if the CRC cannot be extended using acceleration, else returns
|
||||
// the newly extended CRC value (which may also be zero).
|
||||
uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size);
|
||||
|
||||
} // namespace port
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_EXAMPLE_H_
|
||||
223
3rdparty/leveldb/include/port/port_stdcxx.h
vendored
Normal file
223
3rdparty/leveldb/include/port/port_stdcxx.h
vendored
Normal file
@@ -0,0 +1,223 @@
|
||||
// Copyright (c) 2018 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_STDCXX_H_
|
||||
#define STORAGE_LEVELDB_PORT_PORT_STDCXX_H_
|
||||
|
||||
// port/port_config.h availability is automatically detected via __has_include
|
||||
// in newer compilers. If LEVELDB_HAS_PORT_CONFIG_H is defined, it overrides the
|
||||
// configuration detection.
|
||||
#if defined(LEVELDB_HAS_PORT_CONFIG_H)
|
||||
|
||||
#if LEVELDB_HAS_PORT_CONFIG_H
|
||||
#include "port/port_config.h"
|
||||
#endif // LEVELDB_HAS_PORT_CONFIG_H
|
||||
|
||||
#elif defined(__has_include)
|
||||
|
||||
#if __has_include("port/port_config.h")
|
||||
#include "port/port_config.h"
|
||||
#endif // __has_include("port/port_config.h")
|
||||
|
||||
#endif // defined(LEVELDB_HAS_PORT_CONFIG_H)
|
||||
|
||||
#if HAVE_CRC32C
|
||||
#include <crc32c/crc32c.h>
|
||||
#endif // HAVE_CRC32C
|
||||
#if HAVE_SNAPPY
|
||||
#include <snappy.h>
|
||||
#endif // HAVE_SNAPPY
|
||||
#if HAVE_ZSTD
|
||||
#define ZSTD_STATIC_LINKING_ONLY // For ZSTD_compressionParameters.
|
||||
#include <zstd.h>
|
||||
#endif // HAVE_ZSTD
|
||||
|
||||
#include <cassert>
|
||||
#include <condition_variable> // NOLINT
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <mutex> // NOLINT
|
||||
#include <string>
|
||||
|
||||
#include "port/thread_annotations.h"
|
||||
|
||||
namespace leveldb {
|
||||
namespace port {
|
||||
|
||||
class CondVar;
|
||||
|
||||
// Thinly wraps std::mutex.
|
||||
class LOCKABLE Mutex {
|
||||
public:
|
||||
Mutex() = default;
|
||||
~Mutex() = default;
|
||||
|
||||
Mutex(const Mutex&) = delete;
|
||||
Mutex& operator=(const Mutex&) = delete;
|
||||
|
||||
void Lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.lock(); }
|
||||
void Unlock() UNLOCK_FUNCTION() { mu_.unlock(); }
|
||||
void AssertHeld() ASSERT_EXCLUSIVE_LOCK() {}
|
||||
|
||||
private:
|
||||
friend class CondVar;
|
||||
std::mutex mu_;
|
||||
};
|
||||
|
||||
// Thinly wraps std::condition_variable.
|
||||
class CondVar {
|
||||
public:
|
||||
explicit CondVar(Mutex* mu) : mu_(mu) { assert(mu != nullptr); }
|
||||
~CondVar() = default;
|
||||
|
||||
CondVar(const CondVar&) = delete;
|
||||
CondVar& operator=(const CondVar&) = delete;
|
||||
|
||||
void Wait() {
|
||||
std::unique_lock<std::mutex> lock(mu_->mu_, std::adopt_lock);
|
||||
cv_.wait(lock);
|
||||
lock.release();
|
||||
}
|
||||
void Signal() { cv_.notify_one(); }
|
||||
void SignalAll() { cv_.notify_all(); }
|
||||
|
||||
private:
|
||||
std::condition_variable cv_;
|
||||
Mutex* const mu_;
|
||||
};
|
||||
|
||||
inline bool Snappy_Compress(const char* input, size_t length,
|
||||
std::string* output) {
|
||||
#if HAVE_SNAPPY
|
||||
output->resize(snappy::MaxCompressedLength(length));
|
||||
size_t outlen;
|
||||
snappy::RawCompress(input, length, &(*output)[0], &outlen);
|
||||
output->resize(outlen);
|
||||
return true;
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)output;
|
||||
#endif // HAVE_SNAPPY
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
inline bool Snappy_GetUncompressedLength(const char* input, size_t length,
|
||||
size_t* result) {
|
||||
#if HAVE_SNAPPY
|
||||
return snappy::GetUncompressedLength(input, length, result);
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)result;
|
||||
return false;
|
||||
#endif // HAVE_SNAPPY
|
||||
}
|
||||
|
||||
inline bool Snappy_Uncompress(const char* input, size_t length, char* output) {
|
||||
#if HAVE_SNAPPY
|
||||
return snappy::RawUncompress(input, length, output);
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)output;
|
||||
return false;
|
||||
#endif // HAVE_SNAPPY
|
||||
}
|
||||
|
||||
inline bool Zstd_Compress(int level, const char* input, size_t length,
|
||||
std::string* output) {
|
||||
#if HAVE_ZSTD
|
||||
// Get the MaxCompressedLength.
|
||||
size_t outlen = ZSTD_compressBound(length);
|
||||
if (ZSTD_isError(outlen)) {
|
||||
return false;
|
||||
}
|
||||
output->resize(outlen);
|
||||
ZSTD_CCtx* ctx = ZSTD_createCCtx();
|
||||
ZSTD_compressionParameters parameters =
|
||||
ZSTD_getCParams(level, std::max(length, size_t{1}), /*dictSize=*/0);
|
||||
ZSTD_CCtx_setCParams(ctx, parameters);
|
||||
outlen = ZSTD_compress2(ctx, &(*output)[0], output->size(), input, length);
|
||||
ZSTD_freeCCtx(ctx);
|
||||
if (ZSTD_isError(outlen)) {
|
||||
return false;
|
||||
}
|
||||
output->resize(outlen);
|
||||
return true;
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)level;
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)output;
|
||||
return false;
|
||||
#endif // HAVE_ZSTD
|
||||
}
|
||||
|
||||
inline bool Zstd_GetUncompressedLength(const char* input, size_t length,
|
||||
size_t* result) {
|
||||
#if HAVE_ZSTD
|
||||
size_t size = ZSTD_getFrameContentSize(input, length);
|
||||
if (size == 0) return false;
|
||||
*result = size;
|
||||
return true;
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)result;
|
||||
return false;
|
||||
#endif // HAVE_ZSTD
|
||||
}
|
||||
|
||||
inline bool Zstd_Uncompress(const char* input, size_t length, char* output) {
|
||||
#if HAVE_ZSTD
|
||||
size_t outlen;
|
||||
if (!Zstd_GetUncompressedLength(input, length, &outlen)) {
|
||||
return false;
|
||||
}
|
||||
ZSTD_DCtx* ctx = ZSTD_createDCtx();
|
||||
outlen = ZSTD_decompressDCtx(ctx, output, outlen, input, length);
|
||||
ZSTD_freeDCtx(ctx);
|
||||
if (ZSTD_isError(outlen)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)input;
|
||||
(void)length;
|
||||
(void)output;
|
||||
return false;
|
||||
#endif // HAVE_ZSTD
|
||||
}
|
||||
|
||||
inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) {
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)func;
|
||||
(void)arg;
|
||||
return false;
|
||||
}
|
||||
|
||||
inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) {
|
||||
#if HAVE_CRC32C
|
||||
return ::crc32c::Extend(crc, reinterpret_cast<const uint8_t*>(buf), size);
|
||||
#else
|
||||
// Silence compiler warnings about unused arguments.
|
||||
(void)crc;
|
||||
(void)buf;
|
||||
(void)size;
|
||||
return 0;
|
||||
#endif // HAVE_CRC32C
|
||||
}
|
||||
|
||||
} // namespace port
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_PORT_STDCXX_H_
|
||||
108
3rdparty/leveldb/include/port/thread_annotations.h
vendored
Normal file
108
3rdparty/leveldb/include/port/thread_annotations.h
vendored
Normal file
@@ -0,0 +1,108 @@
|
||||
// Copyright (c) 2012 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_THREAD_ANNOTATIONS_H_
|
||||
#define STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_
|
||||
|
||||
// Use Clang's thread safety analysis annotations when available. In other
|
||||
// environments, the macros receive empty definitions.
|
||||
// Usage documentation: https://clang.llvm.org/docs/ThreadSafetyAnalysis.html
|
||||
|
||||
#if !defined(THREAD_ANNOTATION_ATTRIBUTE__)
|
||||
|
||||
#if defined(__clang__)
|
||||
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) __attribute__((x))
|
||||
#else
|
||||
#define THREAD_ANNOTATION_ATTRIBUTE__(x) // no-op
|
||||
#endif
|
||||
|
||||
#endif // !defined(THREAD_ANNOTATION_ATTRIBUTE__)
|
||||
|
||||
#ifndef GUARDED_BY
|
||||
#define GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(guarded_by(x))
|
||||
#endif
|
||||
|
||||
#ifndef PT_GUARDED_BY
|
||||
#define PT_GUARDED_BY(x) THREAD_ANNOTATION_ATTRIBUTE__(pt_guarded_by(x))
|
||||
#endif
|
||||
|
||||
#ifndef ACQUIRED_AFTER
|
||||
#define ACQUIRED_AFTER(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_after(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ACQUIRED_BEFORE
|
||||
#define ACQUIRED_BEFORE(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(acquired_before(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUSIVE_LOCKS_REQUIRED
|
||||
#define EXCLUSIVE_LOCKS_REQUIRED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_locks_required(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SHARED_LOCKS_REQUIRED
|
||||
#define SHARED_LOCKS_REQUIRED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_locks_required(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef LOCKS_EXCLUDED
|
||||
#define LOCKS_EXCLUDED(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(locks_excluded(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef LOCK_RETURNED
|
||||
#define LOCK_RETURNED(x) THREAD_ANNOTATION_ATTRIBUTE__(lock_returned(x))
|
||||
#endif
|
||||
|
||||
#ifndef LOCKABLE
|
||||
#define LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(lockable)
|
||||
#endif
|
||||
|
||||
#ifndef SCOPED_LOCKABLE
|
||||
#define SCOPED_LOCKABLE THREAD_ANNOTATION_ATTRIBUTE__(scoped_lockable)
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUSIVE_LOCK_FUNCTION
|
||||
#define EXCLUSIVE_LOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_lock_function(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SHARED_LOCK_FUNCTION
|
||||
#define SHARED_LOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_lock_function(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef EXCLUSIVE_TRYLOCK_FUNCTION
|
||||
#define EXCLUSIVE_TRYLOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(exclusive_trylock_function(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef SHARED_TRYLOCK_FUNCTION
|
||||
#define SHARED_TRYLOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(shared_trylock_function(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef UNLOCK_FUNCTION
|
||||
#define UNLOCK_FUNCTION(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(unlock_function(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef NO_THREAD_SAFETY_ANALYSIS
|
||||
#define NO_THREAD_SAFETY_ANALYSIS \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(no_thread_safety_analysis)
|
||||
#endif
|
||||
|
||||
#ifndef ASSERT_EXCLUSIVE_LOCK
|
||||
#define ASSERT_EXCLUSIVE_LOCK(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_exclusive_lock(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#ifndef ASSERT_SHARED_LOCK
|
||||
#define ASSERT_SHARED_LOCK(...) \
|
||||
THREAD_ANNOTATION_ATTRIBUTE__(assert_shared_lock(__VA_ARGS__))
|
||||
#endif
|
||||
|
||||
#endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_
|
||||
44
3rdparty/leveldb/include/table/block.h
vendored
Normal file
44
3rdparty/leveldb/include/table/block.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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 <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "leveldb/iterator.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
struct BlockContents;
|
||||
class Comparator;
|
||||
|
||||
class Block {
|
||||
public:
|
||||
// Initialize the block with the specified contents.
|
||||
explicit Block(const BlockContents& contents);
|
||||
|
||||
Block(const Block&) = delete;
|
||||
Block& operator=(const Block&) = delete;
|
||||
|
||||
~Block();
|
||||
|
||||
size_t size() const { return size_; }
|
||||
Iterator* NewIterator(const Comparator* comparator);
|
||||
|
||||
private:
|
||||
class Iter;
|
||||
|
||||
uint32_t NumRestarts() const;
|
||||
|
||||
const char* data_;
|
||||
size_t size_;
|
||||
uint32_t restart_offset_; // Offset in data_ of restart array
|
||||
bool owned_; // Block owns data_[]
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_TABLE_BLOCK_H_
|
||||
54
3rdparty/leveldb/include/table/block_builder.h
vendored
Normal file
54
3rdparty/leveldb/include/table/block_builder.h
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "leveldb/slice.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
struct Options;
|
||||
|
||||
class BlockBuilder {
|
||||
public:
|
||||
explicit BlockBuilder(const Options* options);
|
||||
|
||||
BlockBuilder(const BlockBuilder&) = delete;
|
||||
BlockBuilder& operator=(const BlockBuilder&) = delete;
|
||||
|
||||
// Reset the contents as if the BlockBuilder was just constructed.
|
||||
void Reset();
|
||||
|
||||
// REQUIRES: Finish() has not been called 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_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_TABLE_BLOCK_BUILDER_H_
|
||||
68
3rdparty/leveldb/include/table/filter_block.h
vendored
Normal file
68
3rdparty/leveldb/include/table/filter_block.h
vendored
Normal file
@@ -0,0 +1,68 @@
|
||||
// Copyright (c) 2012 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 filter block is stored near the end of a Table file. It contains
|
||||
// filters (e.g., bloom filters) for all data blocks in the table combined
|
||||
// into a single filter block.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_
|
||||
#define STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "leveldb/slice.h"
|
||||
#include "util/hash.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class FilterPolicy;
|
||||
|
||||
// A FilterBlockBuilder is used to construct all of the filters for a
|
||||
// particular Table. It generates a single string which is stored as
|
||||
// a special block in the Table.
|
||||
//
|
||||
// The sequence of calls to FilterBlockBuilder must match the regexp:
|
||||
// (StartBlock AddKey*)* Finish
|
||||
class FilterBlockBuilder {
|
||||
public:
|
||||
explicit FilterBlockBuilder(const FilterPolicy*);
|
||||
|
||||
FilterBlockBuilder(const FilterBlockBuilder&) = delete;
|
||||
FilterBlockBuilder& operator=(const FilterBlockBuilder&) = delete;
|
||||
|
||||
void StartBlock(uint64_t block_offset);
|
||||
void AddKey(const Slice& key);
|
||||
Slice Finish();
|
||||
|
||||
private:
|
||||
void GenerateFilter();
|
||||
|
||||
const FilterPolicy* policy_;
|
||||
std::string keys_; // Flattened key contents
|
||||
std::vector<size_t> start_; // Starting index in keys_ of each key
|
||||
std::string result_; // Filter data computed so far
|
||||
std::vector<Slice> tmp_keys_; // policy_->CreateFilter() argument
|
||||
std::vector<uint32_t> filter_offsets_;
|
||||
};
|
||||
|
||||
class FilterBlockReader {
|
||||
public:
|
||||
// REQUIRES: "contents" and *policy must stay live while *this is live.
|
||||
FilterBlockReader(const FilterPolicy* policy, const Slice& contents);
|
||||
bool KeyMayMatch(uint64_t block_offset, const Slice& key);
|
||||
|
||||
private:
|
||||
const FilterPolicy* policy_;
|
||||
const char* data_; // Pointer to filter data (at block-start)
|
||||
const char* offset_; // Pointer to beginning of offset array (at block-end)
|
||||
size_t num_; // Number of entries in offset array
|
||||
size_t base_lg_; // Encoding parameter (see kFilterBaseLg in .cc file)
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_TABLE_FILTER_BLOCK_H_
|
||||
99
3rdparty/leveldb/include/table/format.h
vendored
Normal file
99
3rdparty/leveldb/include/table/format.h
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
// 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 <cstdint>
|
||||
#include <string>
|
||||
|
||||
#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:
|
||||
// Maximum encoding length of a BlockHandle
|
||||
enum { kMaxEncodedLength = 10 + 10 };
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
uint64_t offset_;
|
||||
uint64_t size_;
|
||||
};
|
||||
|
||||
// Footer encapsulates the fixed information stored at the tail
|
||||
// end of every table file.
|
||||
class Footer {
|
||||
public:
|
||||
// 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 };
|
||||
|
||||
Footer() = default;
|
||||
|
||||
// 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);
|
||||
|
||||
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;
|
||||
|
||||
struct BlockContents {
|
||||
Slice data; // Actual contents of data
|
||||
bool cachable; // True iff data can be cached
|
||||
bool heap_allocated; // True iff caller should delete[] data.data()
|
||||
};
|
||||
|
||||
// Read the block identified by "handle" from "file". On failure
|
||||
// return non-OK. On success fill *result and return OK.
|
||||
Status ReadBlock(RandomAccessFile* file, const ReadOptions& options,
|
||||
const BlockHandle& handle, BlockContents* result);
|
||||
|
||||
// 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_
|
||||
92
3rdparty/leveldb/include/table/iterator_wrapper.h
vendored
Normal file
92
3rdparty/leveldb/include/table/iterator_wrapper.h
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// 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_
|
||||
|
||||
#include "leveldb/iterator.h"
|
||||
#include "leveldb/slice.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_(nullptr), valid_(false) {}
|
||||
explicit IteratorWrapper(Iterator* iter) : iter_(nullptr) { 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_ == nullptr) {
|
||||
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() != nullptr
|
||||
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_
|
||||
26
3rdparty/leveldb/include/table/merger.h
vendored
Normal file
26
3rdparty/leveldb/include/table/merger.h
vendored
Normal file
@@ -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
|
||||
Iterator* NewMergingIterator(const Comparator* comparator, Iterator** children,
|
||||
int n);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_TABLE_MERGER_H_
|
||||
31
3rdparty/leveldb/include/table/two_level_iterator.h
vendored
Normal file
31
3rdparty/leveldb/include/table/two_level_iterator.h
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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.
|
||||
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_
|
||||
71
3rdparty/leveldb/include/util/arena.h
vendored
Normal file
71
3rdparty/leveldb/include/util/arena.h
vendored
Normal file
@@ -0,0 +1,71 @@
|
||||
// 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 <atomic>
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class Arena {
|
||||
public:
|
||||
Arena();
|
||||
|
||||
Arena(const Arena&) = delete;
|
||||
Arena& operator=(const Arena&) = delete;
|
||||
|
||||
~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.
|
||||
size_t MemoryUsage() const {
|
||||
return memory_usage_.load(std::memory_order_relaxed);
|
||||
}
|
||||
|
||||
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_;
|
||||
|
||||
// Total memory usage of the arena.
|
||||
//
|
||||
// TODO(costan): This member is accessed via atomics, but the others are
|
||||
// accessed without any locking. Is this OK?
|
||||
std::atomic<size_t> memory_usage_;
|
||||
};
|
||||
|
||||
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_
|
||||
122
3rdparty/leveldb/include/util/coding.h
vendored
Normal file
122
3rdparty/leveldb/include/util/coding.h
vendored
Normal file
@@ -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.
|
||||
//
|
||||
// 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 <cstdint>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "leveldb/slice.h"
|
||||
#include "port/port.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
// Standard Put... routines append to a string
|
||||
void PutFixed32(std::string* dst, uint32_t value);
|
||||
void PutFixed64(std::string* dst, uint64_t value);
|
||||
void PutVarint32(std::string* dst, uint32_t value);
|
||||
void PutVarint64(std::string* dst, uint64_t value);
|
||||
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.
|
||||
bool GetVarint32(Slice* input, uint32_t* value);
|
||||
bool GetVarint64(Slice* input, uint64_t* value);
|
||||
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
|
||||
// nullptr on error. These routines only look at bytes in the range
|
||||
// [p..limit-1]
|
||||
const char* GetVarint32Ptr(const char* p, const char* limit, uint32_t* v);
|
||||
const char* GetVarint64Ptr(const char* p, const char* limit, uint64_t* v);
|
||||
|
||||
// Returns the length of the varint32 or varint64 encoding of "v"
|
||||
int VarintLength(uint64_t v);
|
||||
|
||||
// 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
|
||||
char* EncodeVarint32(char* dst, uint32_t value);
|
||||
char* EncodeVarint64(char* dst, uint64_t value);
|
||||
|
||||
// Lower-level versions of Put... that write directly into a character buffer
|
||||
// REQUIRES: dst has enough space for the value being written
|
||||
|
||||
inline void EncodeFixed32(char* dst, uint32_t value) {
|
||||
uint8_t* const buffer = reinterpret_cast<uint8_t*>(dst);
|
||||
|
||||
// Recent clang and gcc optimize this to a single mov / str instruction.
|
||||
buffer[0] = static_cast<uint8_t>(value);
|
||||
buffer[1] = static_cast<uint8_t>(value >> 8);
|
||||
buffer[2] = static_cast<uint8_t>(value >> 16);
|
||||
buffer[3] = static_cast<uint8_t>(value >> 24);
|
||||
}
|
||||
|
||||
inline void EncodeFixed64(char* dst, uint64_t value) {
|
||||
uint8_t* const buffer = reinterpret_cast<uint8_t*>(dst);
|
||||
|
||||
// Recent clang and gcc optimize this to a single mov / str instruction.
|
||||
buffer[0] = static_cast<uint8_t>(value);
|
||||
buffer[1] = static_cast<uint8_t>(value >> 8);
|
||||
buffer[2] = static_cast<uint8_t>(value >> 16);
|
||||
buffer[3] = static_cast<uint8_t>(value >> 24);
|
||||
buffer[4] = static_cast<uint8_t>(value >> 32);
|
||||
buffer[5] = static_cast<uint8_t>(value >> 40);
|
||||
buffer[6] = static_cast<uint8_t>(value >> 48);
|
||||
buffer[7] = static_cast<uint8_t>(value >> 56);
|
||||
}
|
||||
|
||||
// Lower-level versions of Get... that read directly from a character buffer
|
||||
// without any bounds checking.
|
||||
|
||||
inline uint32_t DecodeFixed32(const char* ptr) {
|
||||
const uint8_t* const buffer = reinterpret_cast<const uint8_t*>(ptr);
|
||||
|
||||
// Recent clang and gcc optimize this to a single mov / ldr instruction.
|
||||
return (static_cast<uint32_t>(buffer[0])) |
|
||||
(static_cast<uint32_t>(buffer[1]) << 8) |
|
||||
(static_cast<uint32_t>(buffer[2]) << 16) |
|
||||
(static_cast<uint32_t>(buffer[3]) << 24);
|
||||
}
|
||||
|
||||
inline uint64_t DecodeFixed64(const char* ptr) {
|
||||
const uint8_t* const buffer = reinterpret_cast<const uint8_t*>(ptr);
|
||||
|
||||
// Recent clang and gcc optimize this to a single mov / ldr instruction.
|
||||
return (static_cast<uint64_t>(buffer[0])) |
|
||||
(static_cast<uint64_t>(buffer[1]) << 8) |
|
||||
(static_cast<uint64_t>(buffer[2]) << 16) |
|
||||
(static_cast<uint64_t>(buffer[3]) << 24) |
|
||||
(static_cast<uint64_t>(buffer[4]) << 32) |
|
||||
(static_cast<uint64_t>(buffer[5]) << 40) |
|
||||
(static_cast<uint64_t>(buffer[6]) << 48) |
|
||||
(static_cast<uint64_t>(buffer[7]) << 56);
|
||||
}
|
||||
|
||||
// Internal routine for use by fallback path of GetVarint32Ptr
|
||||
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 uint8_t*>(p));
|
||||
if ((result & 128) == 0) {
|
||||
*value = result;
|
||||
return p + 1;
|
||||
}
|
||||
}
|
||||
return GetVarint32PtrFallback(p, limit, value);
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_CODING_H_
|
||||
43
3rdparty/leveldb/include/util/crc32c.h
vendored
Normal file
43
3rdparty/leveldb/include/util/crc32c.h
vendored
Normal file
@@ -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_UTIL_CRC32C_H_
|
||||
#define STORAGE_LEVELDB_UTIL_CRC32C_H_
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
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.
|
||||
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_
|
||||
28
3rdparty/leveldb/include/util/env_posix_test_helper.h
vendored
Normal file
28
3rdparty/leveldb/include/util/env_posix_test_helper.h
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
// Copyright 2017 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_ENV_POSIX_TEST_HELPER_H_
|
||||
#define STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class EnvPosixTest;
|
||||
|
||||
// A helper for the POSIX Env to facilitate testing.
|
||||
class EnvPosixTestHelper {
|
||||
private:
|
||||
friend class EnvPosixTest;
|
||||
|
||||
// Set the maximum number of read-only files that will be opened.
|
||||
// Must be called before creating an Env.
|
||||
static void SetReadOnlyFDLimit(int limit);
|
||||
|
||||
// Set the maximum number of read-only files that will be mapped via mmap.
|
||||
// Must be called before creating an Env.
|
||||
static void SetReadOnlyMMapLimit(int limit);
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_
|
||||
25
3rdparty/leveldb/include/util/env_windows_test_helper.h
vendored
Normal file
25
3rdparty/leveldb/include/util/env_windows_test_helper.h
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2018 (c) 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_ENV_WINDOWS_TEST_HELPER_H_
|
||||
#define STORAGE_LEVELDB_UTIL_ENV_WINDOWS_TEST_HELPER_H_
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class EnvWindowsTest;
|
||||
|
||||
// A helper for the Windows Env to facilitate testing.
|
||||
class EnvWindowsTestHelper {
|
||||
private:
|
||||
friend class CorruptionTest;
|
||||
friend class EnvWindowsTest;
|
||||
|
||||
// Set the maximum number of read-only files that will be mapped via mmap.
|
||||
// Must be called before creating an Env.
|
||||
static void SetReadOnlyMMapLimit(int limit);
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_ENV_WINDOWS_TEST_HELPER_H_
|
||||
19
3rdparty/leveldb/include/util/hash.h
vendored
Normal file
19
3rdparty/leveldb/include/util/hash.h
vendored
Normal file
@@ -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 <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
uint32_t Hash(const char* data, size_t n, uint32_t seed);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_HASH_H_
|
||||
44
3rdparty/leveldb/include/util/histogram.h
vendored
Normal file
44
3rdparty/leveldb/include/util/histogram.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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:
|
||||
enum { kNumBuckets = 154 };
|
||||
|
||||
double Median() const;
|
||||
double Percentile(double p) const;
|
||||
double Average() const;
|
||||
double StandardDeviation() const;
|
||||
|
||||
static const double kBucketLimit[kNumBuckets];
|
||||
|
||||
double min_;
|
||||
double max_;
|
||||
double num_;
|
||||
double sum_;
|
||||
double sum_squares_;
|
||||
|
||||
double buckets_[kNumBuckets];
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_
|
||||
44
3rdparty/leveldb/include/util/logging.h
vendored
Normal file
44
3rdparty/leveldb/include/util/logging.h
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
// 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 <cstdint>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "port/port.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class Slice;
|
||||
class WritableFile;
|
||||
|
||||
// Append a human-readable printout of "num" to *str
|
||||
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".
|
||||
void AppendEscapedStringTo(std::string* str, const Slice& value);
|
||||
|
||||
// Return a human-readable printout of "num"
|
||||
std::string NumberToString(uint64_t num);
|
||||
|
||||
// Return a human-readable version of "value".
|
||||
// Escapes any non-printable characters found in "value".
|
||||
std::string EscapeString(const Slice& value);
|
||||
|
||||
// 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.
|
||||
bool ConsumeDecimalNumber(Slice* in, uint64_t* val);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_
|
||||
39
3rdparty/leveldb/include/util/mutexlock.h
vendored
Normal file
39
3rdparty/leveldb/include/util/mutexlock.h
vendored
Normal file
@@ -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"
|
||||
#include "port/thread_annotations.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 SCOPED_LOCKABLE MutexLock {
|
||||
public:
|
||||
explicit MutexLock(port::Mutex* mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) {
|
||||
this->mu_->Lock();
|
||||
}
|
||||
~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); }
|
||||
|
||||
MutexLock(const MutexLock&) = delete;
|
||||
MutexLock& operator=(const MutexLock&) = delete;
|
||||
|
||||
private:
|
||||
port::Mutex* const mu_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_
|
||||
46
3rdparty/leveldb/include/util/no_destructor.h
vendored
Normal file
46
3rdparty/leveldb/include/util/no_destructor.h
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) 2018 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_NO_DESTRUCTOR_H_
|
||||
#define STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_
|
||||
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
// Wraps an instance whose destructor is never called.
|
||||
//
|
||||
// This is intended for use with function-level static variables.
|
||||
template <typename InstanceType>
|
||||
class NoDestructor {
|
||||
public:
|
||||
template <typename... ConstructorArgTypes>
|
||||
explicit NoDestructor(ConstructorArgTypes&&... constructor_args) {
|
||||
static_assert(sizeof(instance_storage_) >= sizeof(InstanceType),
|
||||
"instance_storage_ is not large enough to hold the instance");
|
||||
static_assert(
|
||||
alignof(decltype(instance_storage_)) >= alignof(InstanceType),
|
||||
"instance_storage_ does not meet the instance's alignment requirement");
|
||||
new (&instance_storage_)
|
||||
InstanceType(std::forward<ConstructorArgTypes>(constructor_args)...);
|
||||
}
|
||||
|
||||
~NoDestructor() = default;
|
||||
|
||||
NoDestructor(const NoDestructor&) = delete;
|
||||
NoDestructor& operator=(const NoDestructor&) = delete;
|
||||
|
||||
InstanceType* get() {
|
||||
return reinterpret_cast<InstanceType*>(&instance_storage_);
|
||||
}
|
||||
|
||||
private:
|
||||
typename std::aligned_storage<sizeof(InstanceType),
|
||||
alignof(InstanceType)>::type instance_storage_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_NO_DESTRUCTOR_H_
|
||||
130
3rdparty/leveldb/include/util/posix_logger.h
vendored
Normal file
130
3rdparty/leveldb/include/util/posix_logger.h
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
// 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 <sys/time.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class PosixLogger final : public Logger {
|
||||
public:
|
||||
// Creates a logger that writes to the given file.
|
||||
//
|
||||
// The PosixLogger instance takes ownership of the file handle.
|
||||
explicit PosixLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); }
|
||||
|
||||
~PosixLogger() override { std::fclose(fp_); }
|
||||
|
||||
void Logv(const char* format, std::va_list arguments) override {
|
||||
// Record the time as close to the Logv() call as possible.
|
||||
struct ::timeval now_timeval;
|
||||
::gettimeofday(&now_timeval, nullptr);
|
||||
const std::time_t now_seconds = now_timeval.tv_sec;
|
||||
struct std::tm now_components;
|
||||
::localtime_r(&now_seconds, &now_components);
|
||||
|
||||
// Record the thread ID.
|
||||
constexpr const int kMaxThreadIdSize = 32;
|
||||
std::ostringstream thread_stream;
|
||||
thread_stream << std::this_thread::get_id();
|
||||
std::string thread_id = thread_stream.str();
|
||||
if (thread_id.size() > kMaxThreadIdSize) {
|
||||
thread_id.resize(kMaxThreadIdSize);
|
||||
}
|
||||
|
||||
// We first attempt to print into a stack-allocated buffer. If this attempt
|
||||
// fails, we make a second attempt with a dynamically allocated buffer.
|
||||
constexpr const int kStackBufferSize = 512;
|
||||
char stack_buffer[kStackBufferSize];
|
||||
static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),
|
||||
"sizeof(char) is expected to be 1 in C++");
|
||||
|
||||
int dynamic_buffer_size = 0; // Computed in the first iteration.
|
||||
for (int iteration = 0; iteration < 2; ++iteration) {
|
||||
const int buffer_size =
|
||||
(iteration == 0) ? kStackBufferSize : dynamic_buffer_size;
|
||||
char* const buffer =
|
||||
(iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
|
||||
|
||||
// Print the header into the buffer.
|
||||
int buffer_offset = std::snprintf(
|
||||
buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",
|
||||
now_components.tm_year + 1900, now_components.tm_mon + 1,
|
||||
now_components.tm_mday, now_components.tm_hour, now_components.tm_min,
|
||||
now_components.tm_sec, static_cast<int>(now_timeval.tv_usec),
|
||||
thread_id.c_str());
|
||||
|
||||
// The header can be at most 28 characters (10 date + 15 time +
|
||||
// 3 delimiters) plus the thread ID, which should fit comfortably into the
|
||||
// static buffer.
|
||||
assert(buffer_offset <= 28 + kMaxThreadIdSize);
|
||||
static_assert(28 + kMaxThreadIdSize < kStackBufferSize,
|
||||
"stack-allocated buffer may not fit the message header");
|
||||
assert(buffer_offset < buffer_size);
|
||||
|
||||
// Print the message into the buffer.
|
||||
std::va_list arguments_copy;
|
||||
va_copy(arguments_copy, arguments);
|
||||
buffer_offset +=
|
||||
std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset,
|
||||
format, arguments_copy);
|
||||
va_end(arguments_copy);
|
||||
|
||||
// The code below may append a newline at the end of the buffer, which
|
||||
// requires an extra character.
|
||||
if (buffer_offset >= buffer_size - 1) {
|
||||
// The message did not fit into the buffer.
|
||||
if (iteration == 0) {
|
||||
// Re-run the loop and use a dynamically-allocated buffer. The buffer
|
||||
// will be large enough for the log message, an extra newline and a
|
||||
// null terminator.
|
||||
dynamic_buffer_size = buffer_offset + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The dynamically-allocated buffer was incorrectly sized. This should
|
||||
// not happen, assuming a correct implementation of std::(v)snprintf.
|
||||
// Fail in tests, recover by truncating the log message in production.
|
||||
assert(false);
|
||||
buffer_offset = buffer_size - 1;
|
||||
}
|
||||
|
||||
// Add a newline if necessary.
|
||||
if (buffer[buffer_offset - 1] != '\n') {
|
||||
buffer[buffer_offset] = '\n';
|
||||
++buffer_offset;
|
||||
}
|
||||
|
||||
assert(buffer_offset <= buffer_size);
|
||||
std::fwrite(buffer, 1, buffer_offset, fp_);
|
||||
std::fflush(fp_);
|
||||
|
||||
if (iteration != 0) {
|
||||
delete[] buffer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::FILE* const fp_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_
|
||||
63
3rdparty/leveldb/include/util/random.h
vendored
Normal file
63
3rdparty/leveldb/include/util/random.h
vendored
Normal file
@@ -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_UTIL_RANDOM_H_
|
||||
#define STORAGE_LEVELDB_UTIL_RANDOM_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
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) {
|
||||
// Avoid bad seeds.
|
||||
if (seed_ == 0 || seed_ == 2147483647L) {
|
||||
seed_ = 1;
|
||||
}
|
||||
}
|
||||
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_
|
||||
82
3rdparty/leveldb/include/util/testutil.h
vendored
Normal file
82
3rdparty/leveldb/include/util/testutil.h
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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 "gmock/gmock.h"
|
||||
#include "gtest/gtest.h"
|
||||
#include "helpers/memenv/memenv.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/slice.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace leveldb {
|
||||
namespace test {
|
||||
|
||||
MATCHER(IsOK, "") { return arg.ok(); }
|
||||
|
||||
// Macros for testing the results of functions that return leveldb::Status or
|
||||
// absl::StatusOr<T> (for any type T).
|
||||
#define EXPECT_LEVELDB_OK(expression) \
|
||||
EXPECT_THAT(expression, leveldb::test::IsOK())
|
||||
#define ASSERT_LEVELDB_OK(expression) \
|
||||
ASSERT_THAT(expression, leveldb::test::IsOK())
|
||||
|
||||
// Returns the random seed used at the start of the current test run.
|
||||
inline int RandomSeed() {
|
||||
return testing::UnitTest::GetInstance()->random_seed();
|
||||
}
|
||||
|
||||
// Store in *dst a random string of length "len" and return a Slice that
|
||||
// references the generated data.
|
||||
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.).
|
||||
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.
|
||||
Slice CompressibleString(Random* rnd, double compressed_fraction, size_t 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(NewMemEnv(Env::Default())),
|
||||
writable_file_error_(false),
|
||||
num_writable_file_errors_(0) {}
|
||||
~ErrorEnv() override { delete target(); }
|
||||
|
||||
Status NewWritableFile(const std::string& fname,
|
||||
WritableFile** result) override {
|
||||
if (writable_file_error_) {
|
||||
++num_writable_file_errors_;
|
||||
*result = nullptr;
|
||||
return Status::IOError(fname, "fake error");
|
||||
}
|
||||
return target()->NewWritableFile(fname, result);
|
||||
}
|
||||
|
||||
Status NewAppendableFile(const std::string& fname,
|
||||
WritableFile** result) override {
|
||||
if (writable_file_error_) {
|
||||
++num_writable_file_errors_;
|
||||
*result = nullptr;
|
||||
return Status::IOError(fname, "fake error");
|
||||
}
|
||||
return target()->NewAppendableFile(fname, result);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace test
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_
|
||||
124
3rdparty/leveldb/include/util/windows_logger.h
vendored
Normal file
124
3rdparty/leveldb/include/util/windows_logger.h
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// Copyright (c) 2018 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 for the Windows platform.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
|
||||
#define STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <ctime>
|
||||
#include <sstream>
|
||||
#include <thread>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class WindowsLogger final : public Logger {
|
||||
public:
|
||||
// Creates a logger that writes to the given file.
|
||||
//
|
||||
// The PosixLogger instance takes ownership of the file handle.
|
||||
explicit WindowsLogger(std::FILE* fp) : fp_(fp) { assert(fp != nullptr); }
|
||||
|
||||
~WindowsLogger() override { std::fclose(fp_); }
|
||||
|
||||
void Logv(const char* format, std::va_list arguments) override {
|
||||
// Record the time as close to the Logv() call as possible.
|
||||
SYSTEMTIME now_components;
|
||||
::GetLocalTime(&now_components);
|
||||
|
||||
// Record the thread ID.
|
||||
constexpr const int kMaxThreadIdSize = 32;
|
||||
std::ostringstream thread_stream;
|
||||
thread_stream << std::this_thread::get_id();
|
||||
std::string thread_id = thread_stream.str();
|
||||
if (thread_id.size() > kMaxThreadIdSize) {
|
||||
thread_id.resize(kMaxThreadIdSize);
|
||||
}
|
||||
|
||||
// We first attempt to print into a stack-allocated buffer. If this attempt
|
||||
// fails, we make a second attempt with a dynamically allocated buffer.
|
||||
constexpr const int kStackBufferSize = 512;
|
||||
char stack_buffer[kStackBufferSize];
|
||||
static_assert(sizeof(stack_buffer) == static_cast<size_t>(kStackBufferSize),
|
||||
"sizeof(char) is expected to be 1 in C++");
|
||||
|
||||
int dynamic_buffer_size = 0; // Computed in the first iteration.
|
||||
for (int iteration = 0; iteration < 2; ++iteration) {
|
||||
const int buffer_size =
|
||||
(iteration == 0) ? kStackBufferSize : dynamic_buffer_size;
|
||||
char* const buffer =
|
||||
(iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
|
||||
|
||||
// Print the header into the buffer.
|
||||
int buffer_offset = std::snprintf(
|
||||
buffer, buffer_size, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %s ",
|
||||
now_components.wYear, now_components.wMonth, now_components.wDay,
|
||||
now_components.wHour, now_components.wMinute, now_components.wSecond,
|
||||
static_cast<int>(now_components.wMilliseconds * 1000),
|
||||
thread_id.c_str());
|
||||
|
||||
// The header can be at most 28 characters (10 date + 15 time +
|
||||
// 3 delimiters) plus the thread ID, which should fit comfortably into the
|
||||
// static buffer.
|
||||
assert(buffer_offset <= 28 + kMaxThreadIdSize);
|
||||
static_assert(28 + kMaxThreadIdSize < kStackBufferSize,
|
||||
"stack-allocated buffer may not fit the message header");
|
||||
assert(buffer_offset < buffer_size);
|
||||
|
||||
// Print the message into the buffer.
|
||||
std::va_list arguments_copy;
|
||||
va_copy(arguments_copy, arguments);
|
||||
buffer_offset +=
|
||||
std::vsnprintf(buffer + buffer_offset, buffer_size - buffer_offset,
|
||||
format, arguments_copy);
|
||||
va_end(arguments_copy);
|
||||
|
||||
// The code below may append a newline at the end of the buffer, which
|
||||
// requires an extra character.
|
||||
if (buffer_offset >= buffer_size - 1) {
|
||||
// The message did not fit into the buffer.
|
||||
if (iteration == 0) {
|
||||
// Re-run the loop and use a dynamically-allocated buffer. The buffer
|
||||
// will be large enough for the log message, an extra newline and a
|
||||
// null terminator.
|
||||
dynamic_buffer_size = buffer_offset + 2;
|
||||
continue;
|
||||
}
|
||||
|
||||
// The dynamically-allocated buffer was incorrectly sized. This should
|
||||
// not happen, assuming a correct implementation of std::(v)snprintf.
|
||||
// Fail in tests, recover by truncating the log message in production.
|
||||
assert(false);
|
||||
buffer_offset = buffer_size - 1;
|
||||
}
|
||||
|
||||
// Add a newline if necessary.
|
||||
if (buffer[buffer_offset - 1] != '\n') {
|
||||
buffer[buffer_offset] = '\n';
|
||||
++buffer_offset;
|
||||
}
|
||||
|
||||
assert(buffer_offset <= buffer_size);
|
||||
std::fwrite(buffer, 1, buffer_offset, fp_);
|
||||
std::fflush(fp_);
|
||||
|
||||
if (iteration != 0) {
|
||||
delete[] buffer;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::FILE* const fp_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_UTIL_WINDOWS_LOGGER_H_
|
||||
112
3rdparty/leveldb/iterator.h
vendored
Normal file
112
3rdparty/leveldb/iterator.h
vendored
Normal file
@@ -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.
|
||||
//
|
||||
// An iterator yields a sequence of key/value pairs from a source.
|
||||
// The following class defines the interface. Multiple implementations
|
||||
// are provided by this library. In particular, iterators are provided
|
||||
// to access the contents of a Table or a DB.
|
||||
//
|
||||
// Multiple threads can invoke const methods on an Iterator without
|
||||
// external synchronization, but if any of the threads may call a
|
||||
// non-const method, all threads accessing the same Iterator must use
|
||||
// external synchronization.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_ITERATOR_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_ITERATOR_H_
|
||||
|
||||
#include "leveldb/export.h"
|
||||
#include "leveldb/slice.h"
|
||||
#include "leveldb/status.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class LEVELDB_EXPORT Iterator {
|
||||
public:
|
||||
Iterator();
|
||||
|
||||
Iterator(const Iterator&) = delete;
|
||||
Iterator& operator=(const Iterator&) = delete;
|
||||
|
||||
virtual ~Iterator();
|
||||
|
||||
// An iterator is either positioned at a key/value pair, or
|
||||
// not valid. This method returns true iff the iterator is valid.
|
||||
virtual bool Valid() const = 0;
|
||||
|
||||
// Position at the first key in the source. The iterator is Valid()
|
||||
// after this call iff the source is not empty.
|
||||
virtual void SeekToFirst() = 0;
|
||||
|
||||
// Position at the last key in the source. The iterator is
|
||||
// Valid() after this call iff the source is not empty.
|
||||
virtual void SeekToLast() = 0;
|
||||
|
||||
// Position at the first key in the source that is at or past target.
|
||||
// The iterator is Valid() after this call iff the source contains
|
||||
// an entry that comes at or past target.
|
||||
virtual void Seek(const Slice& target) = 0;
|
||||
|
||||
// Moves to the next entry in the source. After this call, Valid() is
|
||||
// true iff the iterator was not positioned at the last entry in the source.
|
||||
// REQUIRES: Valid()
|
||||
virtual void Next() = 0;
|
||||
|
||||
// Moves to the previous entry in the source. After this call, Valid() is
|
||||
// true iff the iterator was not positioned at the first entry in source.
|
||||
// REQUIRES: Valid()
|
||||
virtual void Prev() = 0;
|
||||
|
||||
// Return the key for the current entry. The underlying storage for
|
||||
// the returned slice is valid only until the next modification of
|
||||
// the iterator.
|
||||
// REQUIRES: Valid()
|
||||
virtual Slice key() const = 0;
|
||||
|
||||
// Return the value for the current entry. The underlying storage for
|
||||
// the returned slice is valid only until the next modification of
|
||||
// the iterator.
|
||||
// REQUIRES: Valid()
|
||||
virtual Slice value() const = 0;
|
||||
|
||||
// If an error has occurred, return it. Else return an ok status.
|
||||
virtual Status status() const = 0;
|
||||
|
||||
// Clients are allowed to register function/arg1/arg2 triples that
|
||||
// will be invoked when this iterator is destroyed.
|
||||
//
|
||||
// Note that unlike all of the preceding methods, this method is
|
||||
// not abstract and therefore clients should not override it.
|
||||
using CleanupFunction = void (*)(void* arg1, void* arg2);
|
||||
void RegisterCleanup(CleanupFunction function, void* arg1, void* arg2);
|
||||
|
||||
private:
|
||||
// Cleanup functions are stored in a single-linked list.
|
||||
// The list's head node is inlined in the iterator.
|
||||
struct CleanupNode {
|
||||
// True if the node is not used. Only head nodes might be unused.
|
||||
bool IsEmpty() const { return function == nullptr; }
|
||||
// Invokes the cleanup function.
|
||||
void Run() {
|
||||
assert(function != nullptr);
|
||||
(*function)(arg1, arg2);
|
||||
}
|
||||
|
||||
// The head node is used if the function pointer is not null.
|
||||
CleanupFunction function;
|
||||
void* arg1;
|
||||
void* arg2;
|
||||
CleanupNode* next;
|
||||
};
|
||||
CleanupNode cleanup_head_;
|
||||
};
|
||||
|
||||
// Return an empty iterator (yields nothing).
|
||||
LEVELDB_EXPORT Iterator* NewEmptyIterator();
|
||||
|
||||
// Return an empty iterator with the specified status.
|
||||
LEVELDB_EXPORT Iterator* NewErrorIterator(const Status& status);
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_ITERATOR_H_
|
||||
190
3rdparty/leveldb/options.h
vendored
Normal file
190
3rdparty/leveldb/options.h
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
// 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_INCLUDE_OPTIONS_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_OPTIONS_H_
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class Cache;
|
||||
class Comparator;
|
||||
class Env;
|
||||
class FilterPolicy;
|
||||
class Logger;
|
||||
class Snapshot;
|
||||
|
||||
// DB contents are stored in a set of blocks, each of which holds a
|
||||
// sequence of key,value pairs. Each block may be compressed before
|
||||
// being stored in a file. The following enum describes which
|
||||
// compression method (if any) is used to compress a block.
|
||||
enum CompressionType {
|
||||
// NOTE: do not change the values of existing entries, as these are
|
||||
// part of the persistent format on disk.
|
||||
kNoCompression = 0x0,
|
||||
kSnappyCompression = 0x1,
|
||||
kZstdCompression = 0x2,
|
||||
};
|
||||
|
||||
// Options to control the behavior of a database (passed to DB::Open)
|
||||
struct LEVELDB_EXPORT Options {
|
||||
// Create an Options object with default values for all fields.
|
||||
Options();
|
||||
|
||||
// -------------------
|
||||
// Parameters that affect behavior
|
||||
|
||||
// Comparator used to define the order of keys in the table.
|
||||
// Default: a comparator that uses lexicographic byte-wise ordering
|
||||
//
|
||||
// REQUIRES: The client must ensure that the comparator supplied
|
||||
// here has the same name and orders keys *exactly* the same as the
|
||||
// comparator provided to previous open calls on the same DB.
|
||||
const Comparator* comparator;
|
||||
|
||||
// If true, the database will be created if it is missing.
|
||||
bool create_if_missing = false;
|
||||
|
||||
// If true, an error is raised if the database already exists.
|
||||
bool error_if_exists = false;
|
||||
|
||||
// If true, the implementation will do aggressive checking of the
|
||||
// data it is processing and will stop early if it detects any
|
||||
// errors. This may have unforeseen ramifications: for example, a
|
||||
// corruption of one DB entry may cause a large number of entries to
|
||||
// become unreadable or for the entire DB to become unopenable.
|
||||
bool paranoid_checks = false;
|
||||
|
||||
// Use the specified object to interact with the environment,
|
||||
// e.g. to read/write files, schedule background work, etc.
|
||||
// Default: Env::Default()
|
||||
Env* env;
|
||||
|
||||
// Any internal progress/error information generated by the db will
|
||||
// be written to info_log if it is non-null, or to a file stored
|
||||
// in the same directory as the DB contents if info_log is null.
|
||||
Logger* info_log = nullptr;
|
||||
|
||||
// -------------------
|
||||
// Parameters that affect performance
|
||||
|
||||
// Amount of data to build up in memory (backed by an unsorted log
|
||||
// on disk) before converting to a sorted on-disk file.
|
||||
//
|
||||
// Larger values increase performance, especially during bulk loads.
|
||||
// Up to two write buffers may be held in memory at the same time,
|
||||
// so you may wish to adjust this parameter to control memory usage.
|
||||
// Also, a larger write buffer will result in a longer recovery time
|
||||
// the next time the database is opened.
|
||||
size_t write_buffer_size = 4 * 1024 * 1024;
|
||||
|
||||
// Number of open files that can be used by the DB. You may need to
|
||||
// increase this if your database has a large working set (budget
|
||||
// one open file per 2MB of working set).
|
||||
int max_open_files = 1000;
|
||||
|
||||
// Control over blocks (user data is stored in a set of blocks, and
|
||||
// a block is the unit of reading from disk).
|
||||
|
||||
// If non-null, use the specified cache for blocks.
|
||||
// If null, leveldb will automatically create and use an 8MB internal cache.
|
||||
Cache* block_cache = nullptr;
|
||||
|
||||
// Approximate size of user data packed per block. Note that the
|
||||
// block size specified here corresponds to uncompressed data. The
|
||||
// actual size of the unit read from disk may be smaller if
|
||||
// compression is enabled. This parameter can be changed dynamically.
|
||||
size_t block_size = 4 * 1024;
|
||||
|
||||
// Number of keys between restart points for delta encoding of keys.
|
||||
// This parameter can be changed dynamically. Most clients should
|
||||
// leave this parameter alone.
|
||||
int block_restart_interval = 16;
|
||||
|
||||
// Leveldb will write up to this amount of bytes to a file before
|
||||
// switching to a new one.
|
||||
// Most clients should leave this parameter alone. However if your
|
||||
// filesystem is more efficient with larger files, you could
|
||||
// consider increasing the value. The downside will be longer
|
||||
// compactions and hence longer latency/performance hiccups.
|
||||
// Another reason to increase this parameter might be when you are
|
||||
// initially populating a large database.
|
||||
size_t max_file_size = 2 * 1024 * 1024;
|
||||
|
||||
// Compress blocks using the specified compression algorithm. This
|
||||
// parameter can be changed dynamically.
|
||||
//
|
||||
// Default: kSnappyCompression, which gives lightweight but fast
|
||||
// compression.
|
||||
//
|
||||
// Typical speeds of kSnappyCompression on an Intel(R) Core(TM)2 2.4GHz:
|
||||
// ~200-500MB/s compression
|
||||
// ~400-800MB/s decompression
|
||||
// Note that these speeds are significantly faster than most
|
||||
// persistent storage speeds, and therefore it is typically never
|
||||
// worth switching to kNoCompression. Even if the input data is
|
||||
// incompressible, the kSnappyCompression implementation will
|
||||
// efficiently detect that and will switch to uncompressed mode.
|
||||
CompressionType compression = kSnappyCompression;
|
||||
|
||||
// Compression level for zstd.
|
||||
// Currently only the range [-5,22] is supported. Default is 1.
|
||||
int zstd_compression_level = 1;
|
||||
|
||||
// EXPERIMENTAL: If true, append to existing MANIFEST and log files
|
||||
// when a database is opened. This can significantly speed up open.
|
||||
//
|
||||
// Default: currently false, but may become true later.
|
||||
bool reuse_logs = false;
|
||||
|
||||
// If non-null, use the specified filter policy to reduce disk reads.
|
||||
// Many applications will benefit from passing the result of
|
||||
// NewBloomFilterPolicy() here.
|
||||
const FilterPolicy* filter_policy = nullptr;
|
||||
};
|
||||
|
||||
// Options that control read operations
|
||||
struct LEVELDB_EXPORT ReadOptions {
|
||||
// If true, all data read from underlying storage will be
|
||||
// verified against corresponding checksums.
|
||||
bool verify_checksums = false;
|
||||
|
||||
// Should the data read for this iteration be cached in memory?
|
||||
// Callers may wish to set this field to false for bulk scans.
|
||||
bool fill_cache = true;
|
||||
|
||||
// If "snapshot" is non-null, read as of the supplied snapshot
|
||||
// (which must belong to the DB that is being read and which must
|
||||
// not have been released). If "snapshot" is null, use an implicit
|
||||
// snapshot of the state at the beginning of this read operation.
|
||||
const Snapshot* snapshot = nullptr;
|
||||
};
|
||||
|
||||
// Options that control write operations
|
||||
struct LEVELDB_EXPORT WriteOptions {
|
||||
WriteOptions() = default;
|
||||
|
||||
// If true, the write will be flushed from the operating system
|
||||
// buffer cache (by calling WritableFile::Sync()) before the write
|
||||
// is considered complete. If this flag is true, writes will be
|
||||
// slower.
|
||||
//
|
||||
// If this flag is false, and the machine crashes, some recent
|
||||
// writes may be lost. Note that if it is just the process that
|
||||
// crashes (i.e., the machine does not reboot), no writes will be
|
||||
// lost even if sync==false.
|
||||
//
|
||||
// In other words, a DB write with sync==false has similar
|
||||
// crash semantics as the "write()" system call. A DB write
|
||||
// with sync==true has similar crash semantics to a "write()"
|
||||
// system call followed by "fsync()".
|
||||
bool sync = false;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_OPTIONS_H_
|
||||
114
3rdparty/leveldb/slice.h
vendored
Normal file
114
3rdparty/leveldb/slice.h
vendored
Normal file
@@ -0,0 +1,114 @@
|
||||
// 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.
|
||||
//
|
||||
// Slice is a simple structure containing a pointer into some external
|
||||
// storage and a size. The user of a Slice must ensure that the slice
|
||||
// is not used after the corresponding external storage has been
|
||||
// deallocated.
|
||||
//
|
||||
// Multiple threads can invoke const methods on a Slice without
|
||||
// external synchronization, but if any of the threads may call a
|
||||
// non-const method, all threads accessing the same Slice must use
|
||||
// external synchronization.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_SLICE_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_SLICE_H_
|
||||
|
||||
#include <cassert>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class LEVELDB_EXPORT Slice {
|
||||
public:
|
||||
// Create an empty slice.
|
||||
Slice() : data_(""), size_(0) {}
|
||||
|
||||
// Create a slice that refers to d[0,n-1].
|
||||
Slice(const char* d, size_t n) : data_(d), size_(n) {}
|
||||
|
||||
// Create a slice that refers to the contents of "s"
|
||||
Slice(const std::string& s) : data_(s.data()), size_(s.size()) {}
|
||||
|
||||
// Create a slice that refers to s[0,strlen(s)-1]
|
||||
Slice(const char* s) : data_(s), size_(strlen(s)) {}
|
||||
|
||||
// Intentionally copyable.
|
||||
Slice(const Slice&) = default;
|
||||
Slice& operator=(const Slice&) = default;
|
||||
|
||||
// Return a pointer to the beginning of the referenced data
|
||||
const char* data() const { return data_; }
|
||||
|
||||
// Return the length (in bytes) of the referenced data
|
||||
size_t size() const { return size_; }
|
||||
|
||||
// Return true iff the length of the referenced data is zero
|
||||
bool empty() const { return size_ == 0; }
|
||||
|
||||
// Return the ith byte in the referenced data.
|
||||
// REQUIRES: n < size()
|
||||
char operator[](size_t n) const {
|
||||
assert(n < size());
|
||||
return data_[n];
|
||||
}
|
||||
|
||||
// Change this slice to refer to an empty array
|
||||
void clear() {
|
||||
data_ = "";
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
// Drop the first "n" bytes from this slice.
|
||||
void remove_prefix(size_t n) {
|
||||
assert(n <= size());
|
||||
data_ += n;
|
||||
size_ -= n;
|
||||
}
|
||||
|
||||
// Return a string that contains the copy of the referenced data.
|
||||
std::string ToString() const { return std::string(data_, size_); }
|
||||
|
||||
// Three-way comparison. Returns value:
|
||||
// < 0 iff "*this" < "b",
|
||||
// == 0 iff "*this" == "b",
|
||||
// > 0 iff "*this" > "b"
|
||||
int compare(const Slice& b) const;
|
||||
|
||||
// Return true iff "x" is a prefix of "*this"
|
||||
bool starts_with(const Slice& x) const {
|
||||
return ((size_ >= x.size_) && (memcmp(data_, x.data_, x.size_) == 0));
|
||||
}
|
||||
|
||||
private:
|
||||
const char* data_;
|
||||
size_t size_;
|
||||
};
|
||||
|
||||
inline bool operator==(const Slice& x, const Slice& y) {
|
||||
return ((x.size() == y.size()) &&
|
||||
(memcmp(x.data(), y.data(), x.size()) == 0));
|
||||
}
|
||||
|
||||
inline bool operator!=(const Slice& x, const Slice& y) { return !(x == y); }
|
||||
|
||||
inline int Slice::compare(const Slice& b) const {
|
||||
const size_t min_len = (size_ < b.size_) ? size_ : b.size_;
|
||||
int r = memcmp(data_, b.data_, min_len);
|
||||
if (r == 0) {
|
||||
if (size_ < b.size_)
|
||||
r = -1;
|
||||
else if (size_ > b.size_)
|
||||
r = +1;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_
|
||||
66
3rdparty/leveldb/src/arena.cc
vendored
Normal file
66
3rdparty/leveldb/src/arena.cc
vendored
Normal file
@@ -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.
|
||||
|
||||
#include "util/arena.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
static const int kBlockSize = 4096;
|
||||
|
||||
Arena::Arena()
|
||||
: alloc_ptr_(nullptr), alloc_bytes_remaining_(0), memory_usage_(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*) > 8) ? sizeof(void*) : 8;
|
||||
static_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_.push_back(result);
|
||||
memory_usage_.fetch_add(block_bytes + sizeof(char*),
|
||||
std::memory_order_relaxed);
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
292
3rdparty/leveldb/src/block.cc
vendored
Normal file
292
3rdparty/leveldb/src/block.cc
vendored
Normal file
@@ -0,0 +1,292 @@
|
||||
// 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 <algorithm>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
#include "leveldb/comparator.h"
|
||||
#include "table/format.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
inline uint32_t Block::NumRestarts() const {
|
||||
assert(size_ >= sizeof(uint32_t));
|
||||
return DecodeFixed32(data_ + size_ - sizeof(uint32_t));
|
||||
}
|
||||
|
||||
Block::Block(const BlockContents& contents)
|
||||
: data_(contents.data.data()),
|
||||
size_(contents.data.size()),
|
||||
owned_(contents.heap_allocated) {
|
||||
if (size_ < sizeof(uint32_t)) {
|
||||
size_ = 0; // Error marker
|
||||
} else {
|
||||
size_t max_restarts_allowed = (size_ - sizeof(uint32_t)) / sizeof(uint32_t);
|
||||
if (NumRestarts() > max_restarts_allowed) {
|
||||
// The size is too small for NumRestarts()
|
||||
size_ = 0;
|
||||
} else {
|
||||
restart_offset_ = size_ - (1 + NumRestarts()) * sizeof(uint32_t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Block::~Block() {
|
||||
if (owned_) {
|
||||
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 dereference past "limit".
|
||||
//
|
||||
// If any errors are detected, returns nullptr. 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 nullptr;
|
||||
*shared = reinterpret_cast<const uint8_t*>(p)[0];
|
||||
*non_shared = reinterpret_cast<const uint8_t*>(p)[1];
|
||||
*value_length = reinterpret_cast<const uint8_t*>(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)) == nullptr) return nullptr;
|
||||
if ((p = GetVarint32Ptr(p, limit, non_shared)) == nullptr) return nullptr;
|
||||
if ((p = GetVarint32Ptr(p, limit, value_length)) == nullptr) return nullptr;
|
||||
}
|
||||
|
||||
if (static_cast<uint32_t>(limit - p) < (*non_shared + *value_length)) {
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
bool Valid() const override { return current_ < restarts_; }
|
||||
Status status() const override { return status_; }
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return key_;
|
||||
}
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
return value_;
|
||||
}
|
||||
|
||||
void Next() override {
|
||||
assert(Valid());
|
||||
ParseNextKey();
|
||||
}
|
||||
|
||||
void Prev() override {
|
||||
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);
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) override {
|
||||
// Binary search in restart array to find the last restart point
|
||||
// with a key < target
|
||||
uint32_t left = 0;
|
||||
uint32_t right = num_restarts_ - 1;
|
||||
int current_key_compare = 0;
|
||||
|
||||
if (Valid()) {
|
||||
// If we're already scanning, use the current position as a starting
|
||||
// point. This is beneficial if the key we're seeking to is ahead of the
|
||||
// current position.
|
||||
current_key_compare = Compare(key_, target);
|
||||
if (current_key_compare < 0) {
|
||||
// key_ is smaller than target
|
||||
left = restart_index_;
|
||||
} else if (current_key_compare > 0) {
|
||||
right = restart_index_;
|
||||
} else {
|
||||
// We're seeking to the key we're already at.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
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 == nullptr || (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;
|
||||
}
|
||||
}
|
||||
|
||||
// We might be able to use our current position within the restart block.
|
||||
// This is true if we determined the key we desire is in the current block
|
||||
// and is after than the current key.
|
||||
assert(current_key_compare == 0 || Valid());
|
||||
bool skip_seek = left == restart_index_ && current_key_compare < 0;
|
||||
if (!skip_seek) {
|
||||
SeekToRestartPoint(left);
|
||||
}
|
||||
// Linear search (within restart block) for first key >= target
|
||||
while (true) {
|
||||
if (!ParseNextKey()) {
|
||||
return;
|
||||
}
|
||||
if (Compare(key_, target) >= 0) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void SeekToFirst() override {
|
||||
SeekToRestartPoint(0);
|
||||
ParseNextKey();
|
||||
}
|
||||
|
||||
void SeekToLast() override {
|
||||
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 == nullptr || 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* comparator) {
|
||||
if (size_ < 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(comparator, data_, restart_offset_, num_restarts);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
107
3rdparty/leveldb/src/block_builder.cc
vendored
Normal file
107
3rdparty/leveldb/src/block_builder.cc
vendored
Normal file
@@ -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.
|
||||
//
|
||||
// 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 <cassert>
|
||||
|
||||
#include "leveldb/comparator.h"
|
||||
#include "leveldb/options.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
|
||||
92
3rdparty/leveldb/src/bloom.cc
vendored
Normal file
92
3rdparty/leveldb/src/bloom.cc
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright (c) 2012 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/filter_policy.h"
|
||||
|
||||
#include "leveldb/slice.h"
|
||||
#include "util/hash.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
namespace {
|
||||
static uint32_t BloomHash(const Slice& key) {
|
||||
return Hash(key.data(), key.size(), 0xbc9f1d34);
|
||||
}
|
||||
|
||||
class BloomFilterPolicy : public FilterPolicy {
|
||||
public:
|
||||
explicit BloomFilterPolicy(int bits_per_key) : bits_per_key_(bits_per_key) {
|
||||
// We intentionally round down to reduce probing cost a little bit
|
||||
k_ = static_cast<size_t>(bits_per_key * 0.69); // 0.69 =~ ln(2)
|
||||
if (k_ < 1) k_ = 1;
|
||||
if (k_ > 30) k_ = 30;
|
||||
}
|
||||
|
||||
const char* Name() const override { return "leveldb.BuiltinBloomFilter2"; }
|
||||
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const override {
|
||||
// Compute bloom filter size (in both bits and bytes)
|
||||
size_t bits = n * bits_per_key_;
|
||||
|
||||
// For small n, we can see a very high false positive rate. Fix it
|
||||
// by enforcing a minimum bloom filter length.
|
||||
if (bits < 64) bits = 64;
|
||||
|
||||
size_t bytes = (bits + 7) / 8;
|
||||
bits = bytes * 8;
|
||||
|
||||
const size_t init_size = dst->size();
|
||||
dst->resize(init_size + bytes, 0);
|
||||
dst->push_back(static_cast<char>(k_)); // Remember # of probes in filter
|
||||
char* array = &(*dst)[init_size];
|
||||
for (int i = 0; i < n; i++) {
|
||||
// Use double-hashing to generate a sequence of hash values.
|
||||
// See analysis in [Kirsch,Mitzenmacher 2006].
|
||||
uint32_t h = BloomHash(keys[i]);
|
||||
const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits
|
||||
for (size_t j = 0; j < k_; j++) {
|
||||
const uint32_t bitpos = h % bits;
|
||||
array[bitpos / 8] |= (1 << (bitpos % 8));
|
||||
h += delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool KeyMayMatch(const Slice& key, const Slice& bloom_filter) const override {
|
||||
const size_t len = bloom_filter.size();
|
||||
if (len < 2) return false;
|
||||
|
||||
const char* array = bloom_filter.data();
|
||||
const size_t bits = (len - 1) * 8;
|
||||
|
||||
// Use the encoded k so that we can read filters generated by
|
||||
// bloom filters created using different parameters.
|
||||
const size_t k = array[len - 1];
|
||||
if (k > 30) {
|
||||
// Reserved for potentially new encodings for short bloom filters.
|
||||
// Consider it a match.
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t h = BloomHash(key);
|
||||
const uint32_t delta = (h >> 17) | (h << 15); // Rotate right 17 bits
|
||||
for (size_t j = 0; j < k; j++) {
|
||||
const uint32_t bitpos = h % bits;
|
||||
if ((array[bitpos / 8] & (1 << (bitpos % 8))) == 0) return false;
|
||||
h += delta;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
size_t bits_per_key_;
|
||||
size_t k_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
const FilterPolicy* NewBloomFilterPolicy(int bits_per_key) {
|
||||
return new BloomFilterPolicy(bits_per_key);
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
82
3rdparty/leveldb/src/builder.cc
vendored
Normal file
82
3rdparty/leveldb/src/builder.cc
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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/dbformat.h"
|
||||
#include "db/filename.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());
|
||||
Slice key;
|
||||
for (; iter->Valid(); iter->Next()) {
|
||||
key = iter->key();
|
||||
builder->Add(key, iter->value());
|
||||
}
|
||||
if (!key.empty()) {
|
||||
meta->largest.DecodeFrom(key);
|
||||
}
|
||||
|
||||
// Finish and check for builder errors
|
||||
s = builder->Finish();
|
||||
if (s.ok()) {
|
||||
meta->file_size = builder->FileSize();
|
||||
assert(meta->file_size > 0);
|
||||
}
|
||||
delete builder;
|
||||
|
||||
// Finish and check for file errors
|
||||
if (s.ok()) {
|
||||
s = file->Sync();
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = file->Close();
|
||||
}
|
||||
delete file;
|
||||
file = nullptr;
|
||||
|
||||
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->RemoveFile(fname);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
565
3rdparty/leveldb/src/c.cc
vendored
Normal file
565
3rdparty/leveldb/src/c.cc
vendored
Normal file
@@ -0,0 +1,565 @@
|
||||
// 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 <string.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "leveldb/cache.h"
|
||||
#include "leveldb/comparator.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/filter_policy.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::FilterPolicy;
|
||||
using leveldb::Iterator;
|
||||
using leveldb::kMajorVersion;
|
||||
using leveldb::kMinorVersion;
|
||||
using leveldb::Logger;
|
||||
using leveldb::NewBloomFilterPolicy;
|
||||
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 {
|
||||
~leveldb_comparator_t() override { (*destructor_)(state_); }
|
||||
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
return (*compare_)(state_, a.data(), a.size(), b.data(), b.size());
|
||||
}
|
||||
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
// No-ops since the C binding does not support key shortening methods.
|
||||
void FindShortestSeparator(std::string*, const Slice&) const override {}
|
||||
void FindShortSuccessor(std::string* key) const override {}
|
||||
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
int (*compare_)(void*, const char* a, size_t alen, const char* b,
|
||||
size_t blen);
|
||||
const char* (*name_)(void*);
|
||||
};
|
||||
|
||||
struct leveldb_filterpolicy_t : public FilterPolicy {
|
||||
~leveldb_filterpolicy_t() override { (*destructor_)(state_); }
|
||||
|
||||
const char* Name() const override { return (*name_)(state_); }
|
||||
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const override {
|
||||
std::vector<const char*> key_pointers(n);
|
||||
std::vector<size_t> key_sizes(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
key_pointers[i] = keys[i].data();
|
||||
key_sizes[i] = keys[i].size();
|
||||
}
|
||||
size_t len;
|
||||
char* filter = (*create_)(state_, &key_pointers[0], &key_sizes[0], n, &len);
|
||||
dst->append(filter, len);
|
||||
std::free(filter);
|
||||
}
|
||||
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const override {
|
||||
return (*key_match_)(state_, key.data(), key.size(), filter.data(),
|
||||
filter.size());
|
||||
}
|
||||
|
||||
void* state_;
|
||||
void (*destructor_)(void*);
|
||||
const char* (*name_)(void*);
|
||||
char* (*create_)(void*, const char* const* key_array,
|
||||
const size_t* key_length_array, int num_keys,
|
||||
size_t* filter_length);
|
||||
uint8_t (*key_match_)(void*, const char* key, size_t length,
|
||||
const char* filter, size_t filter_length);
|
||||
};
|
||||
|
||||
struct leveldb_env_t {
|
||||
Env* rep;
|
||||
bool is_default;
|
||||
};
|
||||
|
||||
static bool SaveError(char** errptr, const Status& s) {
|
||||
assert(errptr != nullptr);
|
||||
if (s.ok()) {
|
||||
return false;
|
||||
} else if (*errptr == nullptr) {
|
||||
*errptr = strdup(s.ToString().c_str());
|
||||
} else {
|
||||
// TODO(sanjay): Merge with existing error?
|
||||
std::free(*errptr);
|
||||
*errptr = strdup(s.ToString().c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static char* CopyString(const std::string& str) {
|
||||
char* result =
|
||||
reinterpret_cast<char*>(std::malloc(sizeof(char) * str.size()));
|
||||
std::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 nullptr;
|
||||
}
|
||||
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 = nullptr;
|
||||
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 nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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_compact_range(leveldb_t* db, const char* start_key,
|
||||
size_t start_key_len, const char* limit_key,
|
||||
size_t limit_key_len) {
|
||||
Slice a, b;
|
||||
db->rep->CompactRange(
|
||||
// Pass null Slice if corresponding "const char*" is null
|
||||
(start_key ? (a = Slice(start_key, start_key_len), &a) : nullptr),
|
||||
(limit_key ? (b = Slice(limit_key, limit_key_len), &b) : nullptr));
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
uint8_t 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(const 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);
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
(*put_)(state_, key.data(), key.size(), value.data(), value.size());
|
||||
}
|
||||
void Delete(const Slice& key) override {
|
||||
(*deleted_)(state_, key.data(), key.size());
|
||||
}
|
||||
};
|
||||
H handler;
|
||||
handler.state_ = state;
|
||||
handler.put_ = put;
|
||||
handler.deleted_ = deleted;
|
||||
b->rep.Iterate(&handler);
|
||||
}
|
||||
|
||||
void leveldb_writebatch_append(leveldb_writebatch_t* destination,
|
||||
const leveldb_writebatch_t* source) {
|
||||
destination->rep.Append(source->rep);
|
||||
}
|
||||
|
||||
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_filter_policy(leveldb_options_t* opt,
|
||||
leveldb_filterpolicy_t* policy) {
|
||||
opt->rep.filter_policy = policy;
|
||||
}
|
||||
|
||||
void leveldb_options_set_create_if_missing(leveldb_options_t* opt, uint8_t v) {
|
||||
opt->rep.create_if_missing = v;
|
||||
}
|
||||
|
||||
void leveldb_options_set_error_if_exists(leveldb_options_t* opt, uint8_t v) {
|
||||
opt->rep.error_if_exists = v;
|
||||
}
|
||||
|
||||
void leveldb_options_set_paranoid_checks(leveldb_options_t* opt, uint8_t 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 : nullptr);
|
||||
}
|
||||
|
||||
void leveldb_options_set_info_log(leveldb_options_t* opt, leveldb_logger_t* l) {
|
||||
opt->rep.info_log = (l ? l->rep : nullptr);
|
||||
}
|
||||
|
||||
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_max_file_size(leveldb_options_t* opt, size_t s) {
|
||||
opt->rep.max_file_size = s;
|
||||
}
|
||||
|
||||
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_filterpolicy_t* leveldb_filterpolicy_create(
|
||||
void* state, void (*destructor)(void*),
|
||||
char* (*create_filter)(void*, const char* const* key_array,
|
||||
const size_t* key_length_array, int num_keys,
|
||||
size_t* filter_length),
|
||||
uint8_t (*key_may_match)(void*, const char* key, size_t length,
|
||||
const char* filter, size_t filter_length),
|
||||
const char* (*name)(void*)) {
|
||||
leveldb_filterpolicy_t* result = new leveldb_filterpolicy_t;
|
||||
result->state_ = state;
|
||||
result->destructor_ = destructor;
|
||||
result->create_ = create_filter;
|
||||
result->key_match_ = key_may_match;
|
||||
result->name_ = name;
|
||||
return result;
|
||||
}
|
||||
|
||||
void leveldb_filterpolicy_destroy(leveldb_filterpolicy_t* filter) {
|
||||
delete filter;
|
||||
}
|
||||
|
||||
leveldb_filterpolicy_t* leveldb_filterpolicy_create_bloom(int bits_per_key) {
|
||||
// Make a leveldb_filterpolicy_t, but override all of its methods so
|
||||
// they delegate to a NewBloomFilterPolicy() instead of user
|
||||
// supplied C functions.
|
||||
struct Wrapper : public leveldb_filterpolicy_t {
|
||||
static void DoNothing(void*) {}
|
||||
|
||||
~Wrapper() { delete rep_; }
|
||||
const char* Name() const { return rep_->Name(); }
|
||||
void CreateFilter(const Slice* keys, int n, std::string* dst) const {
|
||||
return rep_->CreateFilter(keys, n, dst);
|
||||
}
|
||||
bool KeyMayMatch(const Slice& key, const Slice& filter) const {
|
||||
return rep_->KeyMayMatch(key, filter);
|
||||
}
|
||||
|
||||
const FilterPolicy* rep_;
|
||||
};
|
||||
Wrapper* wrapper = new Wrapper;
|
||||
wrapper->rep_ = NewBloomFilterPolicy(bits_per_key);
|
||||
wrapper->state_ = nullptr;
|
||||
wrapper->destructor_ = &Wrapper::DoNothing;
|
||||
return wrapper;
|
||||
}
|
||||
|
||||
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,
|
||||
uint8_t v) {
|
||||
opt->rep.verify_checksums = v;
|
||||
}
|
||||
|
||||
void leveldb_readoptions_set_fill_cache(leveldb_readoptions_t* opt, uint8_t 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 : nullptr);
|
||||
}
|
||||
|
||||
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, uint8_t 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;
|
||||
}
|
||||
|
||||
char* leveldb_env_get_test_directory(leveldb_env_t* env) {
|
||||
std::string result;
|
||||
if (!env->rep->GetTestDirectory(&result).ok()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
char* buffer = static_cast<char*>(std::malloc(result.size() + 1));
|
||||
std::memcpy(buffer, result.data(), result.size());
|
||||
buffer[result.size()] = '\0';
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void leveldb_free(void* ptr) { std::free(ptr); }
|
||||
|
||||
int leveldb_major_version() { return kMajorVersion; }
|
||||
|
||||
int leveldb_minor_version() { return kMinorVersion; }
|
||||
|
||||
} // end extern "C"
|
||||
401
3rdparty/leveldb/src/cache.cc
vendored
Normal file
401
3rdparty/leveldb/src/cache.cc
vendored
Normal file
@@ -0,0 +1,401 @@
|
||||
// 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 <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "port/thread_annotations.h"
|
||||
#include "util/hash.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
Cache::~Cache() {}
|
||||
|
||||
namespace {
|
||||
|
||||
// LRU cache implementation
|
||||
//
|
||||
// Cache entries have an "in_cache" boolean indicating whether the cache has a
|
||||
// reference on the entry. The only ways that this can become false without the
|
||||
// entry being passed to its "deleter" are via Erase(), via Insert() when
|
||||
// an element with a duplicate key is inserted, or on destruction of the cache.
|
||||
//
|
||||
// The cache keeps two linked lists of items in the cache. All items in the
|
||||
// cache are in one list or the other, and never both. Items still referenced
|
||||
// by clients but erased from the cache are in neither list. The lists are:
|
||||
// - in-use: contains the items currently referenced by clients, in no
|
||||
// particular order. (This list is used for invariant checking. If we
|
||||
// removed the check, elements that would otherwise be on this list could be
|
||||
// left as disconnected singleton lists.)
|
||||
// - LRU: contains the items not currently referenced by clients, in LRU order
|
||||
// Elements are moved between these lists by the Ref() and Unref() methods,
|
||||
// when they detect an element in the cache acquiring or losing its only
|
||||
// external reference.
|
||||
|
||||
// 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;
|
||||
bool in_cache; // Whether entry is in the cache.
|
||||
uint32_t refs; // References, including cache reference, if present.
|
||||
uint32_t hash; // Hash of key(); used for fast sharding and comparisons
|
||||
char key_data[1]; // Beginning of key
|
||||
|
||||
Slice key() const {
|
||||
// next is only equal to this if the LRU handle is the list head of an
|
||||
// empty list. List heads never have meaningful keys.
|
||||
assert(next != this);
|
||||
|
||||
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_(nullptr) { 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 == nullptr ? nullptr : old->next_hash);
|
||||
*ptr = h;
|
||||
if (old == nullptr) {
|
||||
++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 != nullptr) {
|
||||
*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 != nullptr && ((*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 != nullptr) {
|
||||
LRUHandle* next = h->next_hash;
|
||||
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);
|
||||
void Prune();
|
||||
size_t TotalCharge() const {
|
||||
MutexLock l(&mutex_);
|
||||
return usage_;
|
||||
}
|
||||
|
||||
private:
|
||||
void LRU_Remove(LRUHandle* e);
|
||||
void LRU_Append(LRUHandle* list, LRUHandle* e);
|
||||
void Ref(LRUHandle* e);
|
||||
void Unref(LRUHandle* e);
|
||||
bool FinishErase(LRUHandle* e) EXCLUSIVE_LOCKS_REQUIRED(mutex_);
|
||||
|
||||
// Initialized before use.
|
||||
size_t capacity_;
|
||||
|
||||
// mutex_ protects the following state.
|
||||
mutable port::Mutex mutex_;
|
||||
size_t usage_ GUARDED_BY(mutex_);
|
||||
|
||||
// Dummy head of LRU list.
|
||||
// lru.prev is newest entry, lru.next is oldest entry.
|
||||
// Entries have refs==1 and in_cache==true.
|
||||
LRUHandle lru_ GUARDED_BY(mutex_);
|
||||
|
||||
// Dummy head of in-use list.
|
||||
// Entries are in use by clients, and have refs >= 2 and in_cache==true.
|
||||
LRUHandle in_use_ GUARDED_BY(mutex_);
|
||||
|
||||
HandleTable table_ GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
LRUCache::LRUCache() : capacity_(0), usage_(0) {
|
||||
// Make empty circular linked lists.
|
||||
lru_.next = &lru_;
|
||||
lru_.prev = &lru_;
|
||||
in_use_.next = &in_use_;
|
||||
in_use_.prev = &in_use_;
|
||||
}
|
||||
|
||||
LRUCache::~LRUCache() {
|
||||
assert(in_use_.next == &in_use_); // Error if caller has an unreleased handle
|
||||
for (LRUHandle* e = lru_.next; e != &lru_;) {
|
||||
LRUHandle* next = e->next;
|
||||
assert(e->in_cache);
|
||||
e->in_cache = false;
|
||||
assert(e->refs == 1); // Invariant of lru_ list.
|
||||
Unref(e);
|
||||
e = next;
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCache::Ref(LRUHandle* e) {
|
||||
if (e->refs == 1 && e->in_cache) { // If on lru_ list, move to in_use_ list.
|
||||
LRU_Remove(e);
|
||||
LRU_Append(&in_use_, e);
|
||||
}
|
||||
e->refs++;
|
||||
}
|
||||
|
||||
void LRUCache::Unref(LRUHandle* e) {
|
||||
assert(e->refs > 0);
|
||||
e->refs--;
|
||||
if (e->refs == 0) { // Deallocate.
|
||||
assert(!e->in_cache);
|
||||
(*e->deleter)(e->key(), e->value);
|
||||
free(e);
|
||||
} else if (e->in_cache && e->refs == 1) {
|
||||
// No longer in use; move to lru_ list.
|
||||
LRU_Remove(e);
|
||||
LRU_Append(&lru_, e);
|
||||
}
|
||||
}
|
||||
|
||||
void LRUCache::LRU_Remove(LRUHandle* e) {
|
||||
e->next->prev = e->prev;
|
||||
e->prev->next = e->next;
|
||||
}
|
||||
|
||||
void LRUCache::LRU_Append(LRUHandle* list, LRUHandle* e) {
|
||||
// Make "e" newest entry by inserting just before *list
|
||||
e->next = list;
|
||||
e->prev = list->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 != nullptr) {
|
||||
Ref(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->in_cache = false;
|
||||
e->refs = 1; // for the returned handle.
|
||||
std::memcpy(e->key_data, key.data(), key.size());
|
||||
|
||||
if (capacity_ > 0) {
|
||||
e->refs++; // for the cache's reference.
|
||||
e->in_cache = true;
|
||||
LRU_Append(&in_use_, e);
|
||||
usage_ += charge;
|
||||
FinishErase(table_.Insert(e));
|
||||
} else { // don't cache. (capacity_==0 is supported and turns off caching.)
|
||||
// next is read by key() in an assert, so it must be initialized
|
||||
e->next = nullptr;
|
||||
}
|
||||
while (usage_ > capacity_ && lru_.next != &lru_) {
|
||||
LRUHandle* old = lru_.next;
|
||||
assert(old->refs == 1);
|
||||
bool erased = FinishErase(table_.Remove(old->key(), old->hash));
|
||||
if (!erased) { // to avoid unused variable when compiled NDEBUG
|
||||
assert(erased);
|
||||
}
|
||||
}
|
||||
|
||||
return reinterpret_cast<Cache::Handle*>(e);
|
||||
}
|
||||
|
||||
// If e != nullptr, finish removing *e from the cache; it has already been
|
||||
// removed from the hash table. Return whether e != nullptr.
|
||||
bool LRUCache::FinishErase(LRUHandle* e) {
|
||||
if (e != nullptr) {
|
||||
assert(e->in_cache);
|
||||
LRU_Remove(e);
|
||||
e->in_cache = false;
|
||||
usage_ -= e->charge;
|
||||
Unref(e);
|
||||
}
|
||||
return e != nullptr;
|
||||
}
|
||||
|
||||
void LRUCache::Erase(const Slice& key, uint32_t hash) {
|
||||
MutexLock l(&mutex_);
|
||||
FinishErase(table_.Remove(key, hash));
|
||||
}
|
||||
|
||||
void LRUCache::Prune() {
|
||||
MutexLock l(&mutex_);
|
||||
while (lru_.next != &lru_) {
|
||||
LRUHandle* e = lru_.next;
|
||||
assert(e->refs == 1);
|
||||
bool erased = FinishErase(table_.Remove(e->key(), e->hash));
|
||||
if (!erased) { // to avoid unused variable when compiled NDEBUG
|
||||
assert(erased);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
~ShardedLRUCache() override {}
|
||||
Handle* Insert(const Slice& key, void* value, size_t charge,
|
||||
void (*deleter)(const Slice& key, void* value)) override {
|
||||
const uint32_t hash = HashSlice(key);
|
||||
return shard_[Shard(hash)].Insert(key, hash, value, charge, deleter);
|
||||
}
|
||||
Handle* Lookup(const Slice& key) override {
|
||||
const uint32_t hash = HashSlice(key);
|
||||
return shard_[Shard(hash)].Lookup(key, hash);
|
||||
}
|
||||
void Release(Handle* handle) override {
|
||||
LRUHandle* h = reinterpret_cast<LRUHandle*>(handle);
|
||||
shard_[Shard(h->hash)].Release(handle);
|
||||
}
|
||||
void Erase(const Slice& key) override {
|
||||
const uint32_t hash = HashSlice(key);
|
||||
shard_[Shard(hash)].Erase(key, hash);
|
||||
}
|
||||
void* Value(Handle* handle) override {
|
||||
return reinterpret_cast<LRUHandle*>(handle)->value;
|
||||
}
|
||||
uint64_t NewId() override {
|
||||
MutexLock l(&id_mutex_);
|
||||
return ++(last_id_);
|
||||
}
|
||||
void Prune() override {
|
||||
for (int s = 0; s < kNumShards; s++) {
|
||||
shard_[s].Prune();
|
||||
}
|
||||
}
|
||||
size_t TotalCharge() const override {
|
||||
size_t total = 0;
|
||||
for (int s = 0; s < kNumShards; s++) {
|
||||
total += shard_[s].TotalCharge();
|
||||
}
|
||||
return total;
|
||||
}
|
||||
};
|
||||
|
||||
} // end anonymous namespace
|
||||
|
||||
Cache* NewLRUCache(size_t capacity) { return new ShardedLRUCache(capacity); }
|
||||
|
||||
} // namespace leveldb
|
||||
156
3rdparty/leveldb/src/coding.cc
vendored
Normal file
156
3rdparty/leveldb/src/coding.cc
vendored
Normal file
@@ -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.
|
||||
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
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
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(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;
|
||||
uint8_t* ptr = reinterpret_cast<uint8_t*>(dst);
|
||||
while (v >= B) {
|
||||
*(ptr++) = v | B;
|
||||
v >>= 7;
|
||||
}
|
||||
*(ptr++) = static_cast<uint8_t>(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 uint8_t*>(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 nullptr;
|
||||
}
|
||||
|
||||
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 == nullptr) {
|
||||
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 uint8_t*>(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 nullptr;
|
||||
}
|
||||
|
||||
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 == nullptr) {
|
||||
return false;
|
||||
} else {
|
||||
*input = Slice(q, limit - q);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
75
3rdparty/leveldb/src/comparator.cc
vendored
Normal file
75
3rdparty/leveldb/src/comparator.cc
vendored
Normal file
@@ -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 "leveldb/comparator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
#include "leveldb/slice.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/no_destructor.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
Comparator::~Comparator() = default;
|
||||
|
||||
namespace {
|
||||
class BytewiseComparatorImpl : public Comparator {
|
||||
public:
|
||||
BytewiseComparatorImpl() = default;
|
||||
|
||||
const char* Name() const override { return "leveldb.BytewiseComparator"; }
|
||||
|
||||
int Compare(const Slice& a, const Slice& b) const override {
|
||||
return a.compare(b);
|
||||
}
|
||||
|
||||
void FindShortestSeparator(std::string* start,
|
||||
const Slice& limit) const override {
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FindShortSuccessor(std::string* key) const override {
|
||||
// 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
|
||||
|
||||
const Comparator* BytewiseComparator() {
|
||||
static NoDestructor<BytewiseComparatorImpl> singleton;
|
||||
return singleton.get();
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
380
3rdparty/leveldb/src/crc32c.cc
vendored
Normal file
380
3rdparty/leveldb/src/crc32c.cc
vendored
Normal file
@@ -0,0 +1,380 @@
|
||||
// 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.
|
||||
|
||||
#include "util/crc32c.h"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
|
||||
#include "port/port.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace leveldb {
|
||||
namespace crc32c {
|
||||
|
||||
namespace {
|
||||
|
||||
const uint32_t kByteExtensionTable[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};
|
||||
|
||||
const uint32_t kStrideExtensionTable0[256] = {
|
||||
0x00000000, 0x30d23865, 0x61a470ca, 0x517648af, 0xc348e194, 0xf39ad9f1,
|
||||
0xa2ec915e, 0x923ea93b, 0x837db5d9, 0xb3af8dbc, 0xe2d9c513, 0xd20bfd76,
|
||||
0x4035544d, 0x70e76c28, 0x21912487, 0x11431ce2, 0x03171d43, 0x33c52526,
|
||||
0x62b36d89, 0x526155ec, 0xc05ffcd7, 0xf08dc4b2, 0xa1fb8c1d, 0x9129b478,
|
||||
0x806aa89a, 0xb0b890ff, 0xe1ced850, 0xd11ce035, 0x4322490e, 0x73f0716b,
|
||||
0x228639c4, 0x125401a1, 0x062e3a86, 0x36fc02e3, 0x678a4a4c, 0x57587229,
|
||||
0xc566db12, 0xf5b4e377, 0xa4c2abd8, 0x941093bd, 0x85538f5f, 0xb581b73a,
|
||||
0xe4f7ff95, 0xd425c7f0, 0x461b6ecb, 0x76c956ae, 0x27bf1e01, 0x176d2664,
|
||||
0x053927c5, 0x35eb1fa0, 0x649d570f, 0x544f6f6a, 0xc671c651, 0xf6a3fe34,
|
||||
0xa7d5b69b, 0x97078efe, 0x8644921c, 0xb696aa79, 0xe7e0e2d6, 0xd732dab3,
|
||||
0x450c7388, 0x75de4bed, 0x24a80342, 0x147a3b27, 0x0c5c750c, 0x3c8e4d69,
|
||||
0x6df805c6, 0x5d2a3da3, 0xcf149498, 0xffc6acfd, 0xaeb0e452, 0x9e62dc37,
|
||||
0x8f21c0d5, 0xbff3f8b0, 0xee85b01f, 0xde57887a, 0x4c692141, 0x7cbb1924,
|
||||
0x2dcd518b, 0x1d1f69ee, 0x0f4b684f, 0x3f99502a, 0x6eef1885, 0x5e3d20e0,
|
||||
0xcc0389db, 0xfcd1b1be, 0xada7f911, 0x9d75c174, 0x8c36dd96, 0xbce4e5f3,
|
||||
0xed92ad5c, 0xdd409539, 0x4f7e3c02, 0x7fac0467, 0x2eda4cc8, 0x1e0874ad,
|
||||
0x0a724f8a, 0x3aa077ef, 0x6bd63f40, 0x5b040725, 0xc93aae1e, 0xf9e8967b,
|
||||
0xa89eded4, 0x984ce6b1, 0x890ffa53, 0xb9ddc236, 0xe8ab8a99, 0xd879b2fc,
|
||||
0x4a471bc7, 0x7a9523a2, 0x2be36b0d, 0x1b315368, 0x096552c9, 0x39b76aac,
|
||||
0x68c12203, 0x58131a66, 0xca2db35d, 0xfaff8b38, 0xab89c397, 0x9b5bfbf2,
|
||||
0x8a18e710, 0xbacadf75, 0xebbc97da, 0xdb6eafbf, 0x49500684, 0x79823ee1,
|
||||
0x28f4764e, 0x18264e2b, 0x18b8ea18, 0x286ad27d, 0x791c9ad2, 0x49cea2b7,
|
||||
0xdbf00b8c, 0xeb2233e9, 0xba547b46, 0x8a864323, 0x9bc55fc1, 0xab1767a4,
|
||||
0xfa612f0b, 0xcab3176e, 0x588dbe55, 0x685f8630, 0x3929ce9f, 0x09fbf6fa,
|
||||
0x1baff75b, 0x2b7dcf3e, 0x7a0b8791, 0x4ad9bff4, 0xd8e716cf, 0xe8352eaa,
|
||||
0xb9436605, 0x89915e60, 0x98d24282, 0xa8007ae7, 0xf9763248, 0xc9a40a2d,
|
||||
0x5b9aa316, 0x6b489b73, 0x3a3ed3dc, 0x0aecebb9, 0x1e96d09e, 0x2e44e8fb,
|
||||
0x7f32a054, 0x4fe09831, 0xddde310a, 0xed0c096f, 0xbc7a41c0, 0x8ca879a5,
|
||||
0x9deb6547, 0xad395d22, 0xfc4f158d, 0xcc9d2de8, 0x5ea384d3, 0x6e71bcb6,
|
||||
0x3f07f419, 0x0fd5cc7c, 0x1d81cddd, 0x2d53f5b8, 0x7c25bd17, 0x4cf78572,
|
||||
0xdec92c49, 0xee1b142c, 0xbf6d5c83, 0x8fbf64e6, 0x9efc7804, 0xae2e4061,
|
||||
0xff5808ce, 0xcf8a30ab, 0x5db49990, 0x6d66a1f5, 0x3c10e95a, 0x0cc2d13f,
|
||||
0x14e49f14, 0x2436a771, 0x7540efde, 0x4592d7bb, 0xd7ac7e80, 0xe77e46e5,
|
||||
0xb6080e4a, 0x86da362f, 0x97992acd, 0xa74b12a8, 0xf63d5a07, 0xc6ef6262,
|
||||
0x54d1cb59, 0x6403f33c, 0x3575bb93, 0x05a783f6, 0x17f38257, 0x2721ba32,
|
||||
0x7657f29d, 0x4685caf8, 0xd4bb63c3, 0xe4695ba6, 0xb51f1309, 0x85cd2b6c,
|
||||
0x948e378e, 0xa45c0feb, 0xf52a4744, 0xc5f87f21, 0x57c6d61a, 0x6714ee7f,
|
||||
0x3662a6d0, 0x06b09eb5, 0x12caa592, 0x22189df7, 0x736ed558, 0x43bced3d,
|
||||
0xd1824406, 0xe1507c63, 0xb02634cc, 0x80f40ca9, 0x91b7104b, 0xa165282e,
|
||||
0xf0136081, 0xc0c158e4, 0x52fff1df, 0x622dc9ba, 0x335b8115, 0x0389b970,
|
||||
0x11ddb8d1, 0x210f80b4, 0x7079c81b, 0x40abf07e, 0xd2955945, 0xe2476120,
|
||||
0xb331298f, 0x83e311ea, 0x92a00d08, 0xa272356d, 0xf3047dc2, 0xc3d645a7,
|
||||
0x51e8ec9c, 0x613ad4f9, 0x304c9c56, 0x009ea433};
|
||||
|
||||
const uint32_t kStrideExtensionTable1[256] = {
|
||||
0x00000000, 0x54075546, 0xa80eaa8c, 0xfc09ffca, 0x55f123e9, 0x01f676af,
|
||||
0xfdff8965, 0xa9f8dc23, 0xabe247d2, 0xffe51294, 0x03eced5e, 0x57ebb818,
|
||||
0xfe13643b, 0xaa14317d, 0x561dceb7, 0x021a9bf1, 0x5228f955, 0x062fac13,
|
||||
0xfa2653d9, 0xae21069f, 0x07d9dabc, 0x53de8ffa, 0xafd77030, 0xfbd02576,
|
||||
0xf9cabe87, 0xadcdebc1, 0x51c4140b, 0x05c3414d, 0xac3b9d6e, 0xf83cc828,
|
||||
0x043537e2, 0x503262a4, 0xa451f2aa, 0xf056a7ec, 0x0c5f5826, 0x58580d60,
|
||||
0xf1a0d143, 0xa5a78405, 0x59ae7bcf, 0x0da92e89, 0x0fb3b578, 0x5bb4e03e,
|
||||
0xa7bd1ff4, 0xf3ba4ab2, 0x5a429691, 0x0e45c3d7, 0xf24c3c1d, 0xa64b695b,
|
||||
0xf6790bff, 0xa27e5eb9, 0x5e77a173, 0x0a70f435, 0xa3882816, 0xf78f7d50,
|
||||
0x0b86829a, 0x5f81d7dc, 0x5d9b4c2d, 0x099c196b, 0xf595e6a1, 0xa192b3e7,
|
||||
0x086a6fc4, 0x5c6d3a82, 0xa064c548, 0xf463900e, 0x4d4f93a5, 0x1948c6e3,
|
||||
0xe5413929, 0xb1466c6f, 0x18beb04c, 0x4cb9e50a, 0xb0b01ac0, 0xe4b74f86,
|
||||
0xe6add477, 0xb2aa8131, 0x4ea37efb, 0x1aa42bbd, 0xb35cf79e, 0xe75ba2d8,
|
||||
0x1b525d12, 0x4f550854, 0x1f676af0, 0x4b603fb6, 0xb769c07c, 0xe36e953a,
|
||||
0x4a964919, 0x1e911c5f, 0xe298e395, 0xb69fb6d3, 0xb4852d22, 0xe0827864,
|
||||
0x1c8b87ae, 0x488cd2e8, 0xe1740ecb, 0xb5735b8d, 0x497aa447, 0x1d7df101,
|
||||
0xe91e610f, 0xbd193449, 0x4110cb83, 0x15179ec5, 0xbcef42e6, 0xe8e817a0,
|
||||
0x14e1e86a, 0x40e6bd2c, 0x42fc26dd, 0x16fb739b, 0xeaf28c51, 0xbef5d917,
|
||||
0x170d0534, 0x430a5072, 0xbf03afb8, 0xeb04fafe, 0xbb36985a, 0xef31cd1c,
|
||||
0x133832d6, 0x473f6790, 0xeec7bbb3, 0xbac0eef5, 0x46c9113f, 0x12ce4479,
|
||||
0x10d4df88, 0x44d38ace, 0xb8da7504, 0xecdd2042, 0x4525fc61, 0x1122a927,
|
||||
0xed2b56ed, 0xb92c03ab, 0x9a9f274a, 0xce98720c, 0x32918dc6, 0x6696d880,
|
||||
0xcf6e04a3, 0x9b6951e5, 0x6760ae2f, 0x3367fb69, 0x317d6098, 0x657a35de,
|
||||
0x9973ca14, 0xcd749f52, 0x648c4371, 0x308b1637, 0xcc82e9fd, 0x9885bcbb,
|
||||
0xc8b7de1f, 0x9cb08b59, 0x60b97493, 0x34be21d5, 0x9d46fdf6, 0xc941a8b0,
|
||||
0x3548577a, 0x614f023c, 0x635599cd, 0x3752cc8b, 0xcb5b3341, 0x9f5c6607,
|
||||
0x36a4ba24, 0x62a3ef62, 0x9eaa10a8, 0xcaad45ee, 0x3eced5e0, 0x6ac980a6,
|
||||
0x96c07f6c, 0xc2c72a2a, 0x6b3ff609, 0x3f38a34f, 0xc3315c85, 0x973609c3,
|
||||
0x952c9232, 0xc12bc774, 0x3d2238be, 0x69256df8, 0xc0ddb1db, 0x94dae49d,
|
||||
0x68d31b57, 0x3cd44e11, 0x6ce62cb5, 0x38e179f3, 0xc4e88639, 0x90efd37f,
|
||||
0x39170f5c, 0x6d105a1a, 0x9119a5d0, 0xc51ef096, 0xc7046b67, 0x93033e21,
|
||||
0x6f0ac1eb, 0x3b0d94ad, 0x92f5488e, 0xc6f21dc8, 0x3afbe202, 0x6efcb744,
|
||||
0xd7d0b4ef, 0x83d7e1a9, 0x7fde1e63, 0x2bd94b25, 0x82219706, 0xd626c240,
|
||||
0x2a2f3d8a, 0x7e2868cc, 0x7c32f33d, 0x2835a67b, 0xd43c59b1, 0x803b0cf7,
|
||||
0x29c3d0d4, 0x7dc48592, 0x81cd7a58, 0xd5ca2f1e, 0x85f84dba, 0xd1ff18fc,
|
||||
0x2df6e736, 0x79f1b270, 0xd0096e53, 0x840e3b15, 0x7807c4df, 0x2c009199,
|
||||
0x2e1a0a68, 0x7a1d5f2e, 0x8614a0e4, 0xd213f5a2, 0x7beb2981, 0x2fec7cc7,
|
||||
0xd3e5830d, 0x87e2d64b, 0x73814645, 0x27861303, 0xdb8fecc9, 0x8f88b98f,
|
||||
0x267065ac, 0x727730ea, 0x8e7ecf20, 0xda799a66, 0xd8630197, 0x8c6454d1,
|
||||
0x706dab1b, 0x246afe5d, 0x8d92227e, 0xd9957738, 0x259c88f2, 0x719bddb4,
|
||||
0x21a9bf10, 0x75aeea56, 0x89a7159c, 0xdda040da, 0x74589cf9, 0x205fc9bf,
|
||||
0xdc563675, 0x88516333, 0x8a4bf8c2, 0xde4cad84, 0x2245524e, 0x76420708,
|
||||
0xdfbadb2b, 0x8bbd8e6d, 0x77b471a7, 0x23b324e1};
|
||||
|
||||
const uint32_t kStrideExtensionTable2[256] = {
|
||||
0x00000000, 0x678efd01, 0xcf1dfa02, 0xa8930703, 0x9bd782f5, 0xfc597ff4,
|
||||
0x54ca78f7, 0x334485f6, 0x3243731b, 0x55cd8e1a, 0xfd5e8919, 0x9ad07418,
|
||||
0xa994f1ee, 0xce1a0cef, 0x66890bec, 0x0107f6ed, 0x6486e636, 0x03081b37,
|
||||
0xab9b1c34, 0xcc15e135, 0xff5164c3, 0x98df99c2, 0x304c9ec1, 0x57c263c0,
|
||||
0x56c5952d, 0x314b682c, 0x99d86f2f, 0xfe56922e, 0xcd1217d8, 0xaa9cead9,
|
||||
0x020fedda, 0x658110db, 0xc90dcc6c, 0xae83316d, 0x0610366e, 0x619ecb6f,
|
||||
0x52da4e99, 0x3554b398, 0x9dc7b49b, 0xfa49499a, 0xfb4ebf77, 0x9cc04276,
|
||||
0x34534575, 0x53ddb874, 0x60993d82, 0x0717c083, 0xaf84c780, 0xc80a3a81,
|
||||
0xad8b2a5a, 0xca05d75b, 0x6296d058, 0x05182d59, 0x365ca8af, 0x51d255ae,
|
||||
0xf94152ad, 0x9ecfafac, 0x9fc85941, 0xf846a440, 0x50d5a343, 0x375b5e42,
|
||||
0x041fdbb4, 0x639126b5, 0xcb0221b6, 0xac8cdcb7, 0x97f7ee29, 0xf0791328,
|
||||
0x58ea142b, 0x3f64e92a, 0x0c206cdc, 0x6bae91dd, 0xc33d96de, 0xa4b36bdf,
|
||||
0xa5b49d32, 0xc23a6033, 0x6aa96730, 0x0d279a31, 0x3e631fc7, 0x59ede2c6,
|
||||
0xf17ee5c5, 0x96f018c4, 0xf371081f, 0x94fff51e, 0x3c6cf21d, 0x5be20f1c,
|
||||
0x68a68aea, 0x0f2877eb, 0xa7bb70e8, 0xc0358de9, 0xc1327b04, 0xa6bc8605,
|
||||
0x0e2f8106, 0x69a17c07, 0x5ae5f9f1, 0x3d6b04f0, 0x95f803f3, 0xf276fef2,
|
||||
0x5efa2245, 0x3974df44, 0x91e7d847, 0xf6692546, 0xc52da0b0, 0xa2a35db1,
|
||||
0x0a305ab2, 0x6dbea7b3, 0x6cb9515e, 0x0b37ac5f, 0xa3a4ab5c, 0xc42a565d,
|
||||
0xf76ed3ab, 0x90e02eaa, 0x387329a9, 0x5ffdd4a8, 0x3a7cc473, 0x5df23972,
|
||||
0xf5613e71, 0x92efc370, 0xa1ab4686, 0xc625bb87, 0x6eb6bc84, 0x09384185,
|
||||
0x083fb768, 0x6fb14a69, 0xc7224d6a, 0xa0acb06b, 0x93e8359d, 0xf466c89c,
|
||||
0x5cf5cf9f, 0x3b7b329e, 0x2a03aaa3, 0x4d8d57a2, 0xe51e50a1, 0x8290ada0,
|
||||
0xb1d42856, 0xd65ad557, 0x7ec9d254, 0x19472f55, 0x1840d9b8, 0x7fce24b9,
|
||||
0xd75d23ba, 0xb0d3debb, 0x83975b4d, 0xe419a64c, 0x4c8aa14f, 0x2b045c4e,
|
||||
0x4e854c95, 0x290bb194, 0x8198b697, 0xe6164b96, 0xd552ce60, 0xb2dc3361,
|
||||
0x1a4f3462, 0x7dc1c963, 0x7cc63f8e, 0x1b48c28f, 0xb3dbc58c, 0xd455388d,
|
||||
0xe711bd7b, 0x809f407a, 0x280c4779, 0x4f82ba78, 0xe30e66cf, 0x84809bce,
|
||||
0x2c139ccd, 0x4b9d61cc, 0x78d9e43a, 0x1f57193b, 0xb7c41e38, 0xd04ae339,
|
||||
0xd14d15d4, 0xb6c3e8d5, 0x1e50efd6, 0x79de12d7, 0x4a9a9721, 0x2d146a20,
|
||||
0x85876d23, 0xe2099022, 0x878880f9, 0xe0067df8, 0x48957afb, 0x2f1b87fa,
|
||||
0x1c5f020c, 0x7bd1ff0d, 0xd342f80e, 0xb4cc050f, 0xb5cbf3e2, 0xd2450ee3,
|
||||
0x7ad609e0, 0x1d58f4e1, 0x2e1c7117, 0x49928c16, 0xe1018b15, 0x868f7614,
|
||||
0xbdf4448a, 0xda7ab98b, 0x72e9be88, 0x15674389, 0x2623c67f, 0x41ad3b7e,
|
||||
0xe93e3c7d, 0x8eb0c17c, 0x8fb73791, 0xe839ca90, 0x40aacd93, 0x27243092,
|
||||
0x1460b564, 0x73ee4865, 0xdb7d4f66, 0xbcf3b267, 0xd972a2bc, 0xbefc5fbd,
|
||||
0x166f58be, 0x71e1a5bf, 0x42a52049, 0x252bdd48, 0x8db8da4b, 0xea36274a,
|
||||
0xeb31d1a7, 0x8cbf2ca6, 0x242c2ba5, 0x43a2d6a4, 0x70e65352, 0x1768ae53,
|
||||
0xbffba950, 0xd8755451, 0x74f988e6, 0x137775e7, 0xbbe472e4, 0xdc6a8fe5,
|
||||
0xef2e0a13, 0x88a0f712, 0x2033f011, 0x47bd0d10, 0x46bafbfd, 0x213406fc,
|
||||
0x89a701ff, 0xee29fcfe, 0xdd6d7908, 0xbae38409, 0x1270830a, 0x75fe7e0b,
|
||||
0x107f6ed0, 0x77f193d1, 0xdf6294d2, 0xb8ec69d3, 0x8ba8ec25, 0xec261124,
|
||||
0x44b51627, 0x233beb26, 0x223c1dcb, 0x45b2e0ca, 0xed21e7c9, 0x8aaf1ac8,
|
||||
0xb9eb9f3e, 0xde65623f, 0x76f6653c, 0x1178983d};
|
||||
|
||||
const uint32_t kStrideExtensionTable3[256] = {
|
||||
0x00000000, 0xf20c0dfe, 0xe1f46d0d, 0x13f860f3, 0xc604aceb, 0x3408a115,
|
||||
0x27f0c1e6, 0xd5fccc18, 0x89e52f27, 0x7be922d9, 0x6811422a, 0x9a1d4fd4,
|
||||
0x4fe183cc, 0xbded8e32, 0xae15eec1, 0x5c19e33f, 0x162628bf, 0xe42a2541,
|
||||
0xf7d245b2, 0x05de484c, 0xd0228454, 0x222e89aa, 0x31d6e959, 0xc3dae4a7,
|
||||
0x9fc30798, 0x6dcf0a66, 0x7e376a95, 0x8c3b676b, 0x59c7ab73, 0xabcba68d,
|
||||
0xb833c67e, 0x4a3fcb80, 0x2c4c517e, 0xde405c80, 0xcdb83c73, 0x3fb4318d,
|
||||
0xea48fd95, 0x1844f06b, 0x0bbc9098, 0xf9b09d66, 0xa5a97e59, 0x57a573a7,
|
||||
0x445d1354, 0xb6511eaa, 0x63add2b2, 0x91a1df4c, 0x8259bfbf, 0x7055b241,
|
||||
0x3a6a79c1, 0xc866743f, 0xdb9e14cc, 0x29921932, 0xfc6ed52a, 0x0e62d8d4,
|
||||
0x1d9ab827, 0xef96b5d9, 0xb38f56e6, 0x41835b18, 0x527b3beb, 0xa0773615,
|
||||
0x758bfa0d, 0x8787f7f3, 0x947f9700, 0x66739afe, 0x5898a2fc, 0xaa94af02,
|
||||
0xb96ccff1, 0x4b60c20f, 0x9e9c0e17, 0x6c9003e9, 0x7f68631a, 0x8d646ee4,
|
||||
0xd17d8ddb, 0x23718025, 0x3089e0d6, 0xc285ed28, 0x17792130, 0xe5752cce,
|
||||
0xf68d4c3d, 0x048141c3, 0x4ebe8a43, 0xbcb287bd, 0xaf4ae74e, 0x5d46eab0,
|
||||
0x88ba26a8, 0x7ab62b56, 0x694e4ba5, 0x9b42465b, 0xc75ba564, 0x3557a89a,
|
||||
0x26afc869, 0xd4a3c597, 0x015f098f, 0xf3530471, 0xe0ab6482, 0x12a7697c,
|
||||
0x74d4f382, 0x86d8fe7c, 0x95209e8f, 0x672c9371, 0xb2d05f69, 0x40dc5297,
|
||||
0x53243264, 0xa1283f9a, 0xfd31dca5, 0x0f3dd15b, 0x1cc5b1a8, 0xeec9bc56,
|
||||
0x3b35704e, 0xc9397db0, 0xdac11d43, 0x28cd10bd, 0x62f2db3d, 0x90fed6c3,
|
||||
0x8306b630, 0x710abbce, 0xa4f677d6, 0x56fa7a28, 0x45021adb, 0xb70e1725,
|
||||
0xeb17f41a, 0x191bf9e4, 0x0ae39917, 0xf8ef94e9, 0x2d1358f1, 0xdf1f550f,
|
||||
0xcce735fc, 0x3eeb3802, 0xb13145f8, 0x433d4806, 0x50c528f5, 0xa2c9250b,
|
||||
0x7735e913, 0x8539e4ed, 0x96c1841e, 0x64cd89e0, 0x38d46adf, 0xcad86721,
|
||||
0xd92007d2, 0x2b2c0a2c, 0xfed0c634, 0x0cdccbca, 0x1f24ab39, 0xed28a6c7,
|
||||
0xa7176d47, 0x551b60b9, 0x46e3004a, 0xb4ef0db4, 0x6113c1ac, 0x931fcc52,
|
||||
0x80e7aca1, 0x72eba15f, 0x2ef24260, 0xdcfe4f9e, 0xcf062f6d, 0x3d0a2293,
|
||||
0xe8f6ee8b, 0x1afae375, 0x09028386, 0xfb0e8e78, 0x9d7d1486, 0x6f711978,
|
||||
0x7c89798b, 0x8e857475, 0x5b79b86d, 0xa975b593, 0xba8dd560, 0x4881d89e,
|
||||
0x14983ba1, 0xe694365f, 0xf56c56ac, 0x07605b52, 0xd29c974a, 0x20909ab4,
|
||||
0x3368fa47, 0xc164f7b9, 0x8b5b3c39, 0x795731c7, 0x6aaf5134, 0x98a35cca,
|
||||
0x4d5f90d2, 0xbf539d2c, 0xacabfddf, 0x5ea7f021, 0x02be131e, 0xf0b21ee0,
|
||||
0xe34a7e13, 0x114673ed, 0xc4babff5, 0x36b6b20b, 0x254ed2f8, 0xd742df06,
|
||||
0xe9a9e704, 0x1ba5eafa, 0x085d8a09, 0xfa5187f7, 0x2fad4bef, 0xdda14611,
|
||||
0xce5926e2, 0x3c552b1c, 0x604cc823, 0x9240c5dd, 0x81b8a52e, 0x73b4a8d0,
|
||||
0xa64864c8, 0x54446936, 0x47bc09c5, 0xb5b0043b, 0xff8fcfbb, 0x0d83c245,
|
||||
0x1e7ba2b6, 0xec77af48, 0x398b6350, 0xcb876eae, 0xd87f0e5d, 0x2a7303a3,
|
||||
0x766ae09c, 0x8466ed62, 0x979e8d91, 0x6592806f, 0xb06e4c77, 0x42624189,
|
||||
0x519a217a, 0xa3962c84, 0xc5e5b67a, 0x37e9bb84, 0x2411db77, 0xd61dd689,
|
||||
0x03e11a91, 0xf1ed176f, 0xe215779c, 0x10197a62, 0x4c00995d, 0xbe0c94a3,
|
||||
0xadf4f450, 0x5ff8f9ae, 0x8a0435b6, 0x78083848, 0x6bf058bb, 0x99fc5545,
|
||||
0xd3c39ec5, 0x21cf933b, 0x3237f3c8, 0xc03bfe36, 0x15c7322e, 0xe7cb3fd0,
|
||||
0xf4335f23, 0x063f52dd, 0x5a26b1e2, 0xa82abc1c, 0xbbd2dcef, 0x49ded111,
|
||||
0x9c221d09, 0x6e2e10f7, 0x7dd67004, 0x8fda7dfa};
|
||||
|
||||
// CRCs are pre- and post- conditioned by xoring with all ones.
|
||||
static constexpr const uint32_t kCRC32Xor = static_cast<uint32_t>(0xffffffffU);
|
||||
|
||||
// Reads a little-endian 32-bit integer from a 32-bit-aligned buffer.
|
||||
inline uint32_t ReadUint32LE(const uint8_t* buffer) {
|
||||
return DecodeFixed32(reinterpret_cast<const char*>(buffer));
|
||||
}
|
||||
|
||||
// Returns the smallest address >= the given address that is aligned to N bytes.
|
||||
//
|
||||
// N must be a power of two.
|
||||
template <int N>
|
||||
constexpr inline const uint8_t* RoundUp(const uint8_t* pointer) {
|
||||
return reinterpret_cast<uint8_t*>(
|
||||
(reinterpret_cast<uintptr_t>(pointer) + (N - 1)) &
|
||||
~static_cast<uintptr_t>(N - 1));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
// Determine if the CPU running this program can accelerate the CRC32C
|
||||
// calculation.
|
||||
static bool CanAccelerateCRC32C() {
|
||||
// port::AcceleretedCRC32C returns zero when unable to accelerate.
|
||||
static const char kTestCRCBuffer[] = "TestCRCBuffer";
|
||||
static const char kBufSize = sizeof(kTestCRCBuffer) - 1;
|
||||
static const uint32_t kTestCRCValue = 0xdcbc59fa;
|
||||
|
||||
return port::AcceleratedCRC32C(0, kTestCRCBuffer, kBufSize) == kTestCRCValue;
|
||||
}
|
||||
|
||||
uint32_t Extend(uint32_t crc, const char* data, size_t n) {
|
||||
static bool accelerate = CanAccelerateCRC32C();
|
||||
if (accelerate) {
|
||||
return port::AcceleratedCRC32C(crc, data, n);
|
||||
}
|
||||
|
||||
const uint8_t* p = reinterpret_cast<const uint8_t*>(data);
|
||||
const uint8_t* e = p + n;
|
||||
uint32_t l = crc ^ kCRC32Xor;
|
||||
|
||||
// Process one byte at a time.
|
||||
#define STEP1 \
|
||||
do { \
|
||||
int c = (l & 0xff) ^ *p++; \
|
||||
l = kByteExtensionTable[c] ^ (l >> 8); \
|
||||
} while (0)
|
||||
|
||||
// Process one of the 4 strides of 4-byte data.
|
||||
#define STEP4(s) \
|
||||
do { \
|
||||
crc##s = ReadUint32LE(p + s * 4) ^ kStrideExtensionTable3[crc##s & 0xff] ^ \
|
||||
kStrideExtensionTable2[(crc##s >> 8) & 0xff] ^ \
|
||||
kStrideExtensionTable1[(crc##s >> 16) & 0xff] ^ \
|
||||
kStrideExtensionTable0[crc##s >> 24]; \
|
||||
} while (0)
|
||||
|
||||
// Process a 16-byte swath of 4 strides, each of which has 4 bytes of data.
|
||||
#define STEP16 \
|
||||
do { \
|
||||
STEP4(0); \
|
||||
STEP4(1); \
|
||||
STEP4(2); \
|
||||
STEP4(3); \
|
||||
p += 16; \
|
||||
} while (0)
|
||||
|
||||
// Process 4 bytes that were already loaded into a word.
|
||||
#define STEP4W(w) \
|
||||
do { \
|
||||
w ^= l; \
|
||||
for (size_t i = 0; i < 4; ++i) { \
|
||||
w = (w >> 8) ^ kByteExtensionTable[w & 0xff]; \
|
||||
} \
|
||||
l = w; \
|
||||
} while (0)
|
||||
|
||||
// Point x at first 4-byte aligned byte in the buffer. This might be past the
|
||||
// end of the buffer.
|
||||
const uint8_t* x = RoundUp<4>(p);
|
||||
if (x <= e) {
|
||||
// Process bytes p is 4-byte aligned.
|
||||
while (p != x) {
|
||||
STEP1;
|
||||
}
|
||||
}
|
||||
|
||||
if ((e - p) >= 16) {
|
||||
// Load a 16-byte swath into the stride partial results.
|
||||
uint32_t crc0 = ReadUint32LE(p + 0 * 4) ^ l;
|
||||
uint32_t crc1 = ReadUint32LE(p + 1 * 4);
|
||||
uint32_t crc2 = ReadUint32LE(p + 2 * 4);
|
||||
uint32_t crc3 = ReadUint32LE(p + 3 * 4);
|
||||
p += 16;
|
||||
|
||||
// It is possible to get better speeds (at least on x86) by interleaving
|
||||
// prefetching 256 bytes ahead with processing 64 bytes at a time. See the
|
||||
// portable implementation in https://github.com/google/crc32c/.
|
||||
|
||||
// Process one 16-byte swath at a time.
|
||||
while ((e - p) >= 16) {
|
||||
STEP16;
|
||||
}
|
||||
|
||||
// Advance one word at a time as far as possible.
|
||||
while ((e - p) >= 4) {
|
||||
STEP4(0);
|
||||
uint32_t tmp = crc0;
|
||||
crc0 = crc1;
|
||||
crc1 = crc2;
|
||||
crc2 = crc3;
|
||||
crc3 = tmp;
|
||||
p += 4;
|
||||
}
|
||||
|
||||
// Combine the 4 partial stride results.
|
||||
l = 0;
|
||||
STEP4W(crc0);
|
||||
STEP4W(crc1);
|
||||
STEP4W(crc2);
|
||||
STEP4W(crc3);
|
||||
}
|
||||
|
||||
// Process the last few bytes.
|
||||
while (p != e) {
|
||||
STEP1;
|
||||
}
|
||||
#undef STEP4W
|
||||
#undef STEP16
|
||||
#undef STEP4
|
||||
#undef STEP1
|
||||
return l ^ kCRC32Xor;
|
||||
}
|
||||
|
||||
} // namespace crc32c
|
||||
} // namespace leveldb
|
||||
1573
3rdparty/leveldb/src/db_impl.cc
vendored
Normal file
1573
3rdparty/leveldb/src/db_impl.cc
vendored
Normal file
File diff suppressed because it is too large
Load Diff
318
3rdparty/leveldb/src/db_iter.cc
vendored
Normal file
318
3rdparty/leveldb/src/db_iter.cc
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
// 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/db_impl.h"
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/iterator.h"
|
||||
#include "port/port.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/random.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
#if 0
|
||||
static void DumpInternalIter(Iterator* iter) {
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
ParsedInternalKey k;
|
||||
if (!ParseInternalKey(iter->key(), &k)) {
|
||||
std::fprintf(stderr, "Corrupt '%s'\n", EscapeString(iter->key()).c_str());
|
||||
} else {
|
||||
std::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(DBImpl* db, const Comparator* cmp, Iterator* iter, SequenceNumber s,
|
||||
uint32_t seed)
|
||||
: db_(db),
|
||||
user_comparator_(cmp),
|
||||
iter_(iter),
|
||||
sequence_(s),
|
||||
direction_(kForward),
|
||||
valid_(false),
|
||||
rnd_(seed),
|
||||
bytes_until_read_sampling_(RandomCompactionPeriod()) {}
|
||||
|
||||
DBIter(const DBIter&) = delete;
|
||||
DBIter& operator=(const DBIter&) = delete;
|
||||
|
||||
~DBIter() override { delete iter_; }
|
||||
bool Valid() const override { return valid_; }
|
||||
Slice key() const override {
|
||||
assert(valid_);
|
||||
return (direction_ == kForward) ? ExtractUserKey(iter_->key()) : saved_key_;
|
||||
}
|
||||
Slice value() const override {
|
||||
assert(valid_);
|
||||
return (direction_ == kForward) ? iter_->value() : saved_value_;
|
||||
}
|
||||
Status status() const override {
|
||||
if (status_.ok()) {
|
||||
return iter_->status();
|
||||
} else {
|
||||
return status_;
|
||||
}
|
||||
}
|
||||
|
||||
void Next() override;
|
||||
void Prev() override;
|
||||
void Seek(const Slice& target) override;
|
||||
void SeekToFirst() override;
|
||||
void SeekToLast() override;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
// Picks the number of bytes that can be read until a compaction is scheduled.
|
||||
size_t RandomCompactionPeriod() {
|
||||
return rnd_.Uniform(2 * config::kReadBytesPeriod);
|
||||
}
|
||||
|
||||
DBImpl* db_;
|
||||
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_;
|
||||
Random rnd_;
|
||||
size_t bytes_until_read_sampling_;
|
||||
};
|
||||
|
||||
inline bool DBIter::ParseKey(ParsedInternalKey* ikey) {
|
||||
Slice k = iter_->key();
|
||||
|
||||
size_t bytes_read = k.size() + iter_->value().size();
|
||||
while (bytes_until_read_sampling_ < bytes_read) {
|
||||
bytes_until_read_sampling_ += RandomCompactionPeriod();
|
||||
db_->RecordReadSample(k);
|
||||
}
|
||||
assert(bytes_until_read_sampling_ >= bytes_read);
|
||||
bytes_until_read_sampling_ -= bytes_read;
|
||||
|
||||
if (!ParseInternalKey(k, 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;
|
||||
}
|
||||
// saved_key_ already contains the key to skip past.
|
||||
} else {
|
||||
// Store in saved_key_ the current key so we skip it below.
|
||||
SaveKey(ExtractUserKey(iter_->key()), &saved_key_);
|
||||
|
||||
// iter_ is pointing to current key. We can now safely move to the next to
|
||||
// avoid checking current key.
|
||||
iter_->Next();
|
||||
if (!iter_->Valid()) {
|
||||
valid_ = false;
|
||||
saved_key_.clear();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
FindNextUserEntry(true, &saved_key_);
|
||||
}
|
||||
|
||||
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(DBImpl* db, const Comparator* user_key_comparator,
|
||||
Iterator* internal_iter, SequenceNumber sequence,
|
||||
uint32_t seed) {
|
||||
return new DBIter(db, user_key_comparator, internal_iter, sequence, seed);
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
136
3rdparty/leveldb/src/dbformat.cc
vendored
Normal file
136
3rdparty/leveldb/src/dbformat.cc
vendored
Normal file
@@ -0,0 +1,136 @@
|
||||
// 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 <cstdio>
|
||||
#include <sstream>
|
||||
|
||||
#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 {
|
||||
std::ostringstream ss;
|
||||
ss << '\'' << EscapeString(user_key.ToString()) << "' @ " << sequence << " : "
|
||||
<< static_cast<int>(type);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string InternalKey::DebugString() const {
|
||||
ParsedInternalKey parsed;
|
||||
if (ParseInternalKey(rep_, &parsed)) {
|
||||
return parsed.DebugString();
|
||||
}
|
||||
std::ostringstream ss;
|
||||
ss << "(bad)" << EscapeString(rep_);
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
const char* InternalFilterPolicy::Name() const { return user_policy_->Name(); }
|
||||
|
||||
void InternalFilterPolicy::CreateFilter(const Slice* keys, int n,
|
||||
std::string* dst) const {
|
||||
// We rely on the fact that the code in table.cc does not mind us
|
||||
// adjusting keys[].
|
||||
Slice* mkey = const_cast<Slice*>(keys);
|
||||
for (int i = 0; i < n; i++) {
|
||||
mkey[i] = ExtractUserKey(keys[i]);
|
||||
// TODO(sanjay): Suppress dups?
|
||||
}
|
||||
user_policy_->CreateFilter(keys, n, dst);
|
||||
}
|
||||
|
||||
bool InternalFilterPolicy::KeyMayMatch(const Slice& key, const Slice& f) const {
|
||||
return user_policy_->KeyMayMatch(ExtractUserKey(key), f);
|
||||
}
|
||||
|
||||
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;
|
||||
std::memcpy(dst, user_key.data(), usize);
|
||||
dst += usize;
|
||||
EncodeFixed64(dst, PackSequenceAndType(s, kValueTypeForSeek));
|
||||
dst += 8;
|
||||
end_ = dst;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
232
3rdparty/leveldb/src/dumpfile.cc
vendored
Normal file
232
3rdparty/leveldb/src/dumpfile.cc
vendored
Normal file
@@ -0,0 +1,232 @@
|
||||
// Copyright (c) 2012 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/dumpfile.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "db/dbformat.h"
|
||||
#include "db/filename.h"
|
||||
#include "db/log_reader.h"
|
||||
#include "db/version_edit.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/iterator.h"
|
||||
#include "leveldb/options.h"
|
||||
#include "leveldb/status.h"
|
||||
#include "leveldb/table.h"
|
||||
#include "leveldb/write_batch.h"
|
||||
#include "util/logging.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
namespace {
|
||||
|
||||
bool GuessType(const std::string& fname, FileType* type) {
|
||||
size_t pos = fname.rfind('/');
|
||||
std::string basename;
|
||||
if (pos == std::string::npos) {
|
||||
basename = fname;
|
||||
} else {
|
||||
basename = std::string(fname.data() + pos + 1, fname.size() - pos - 1);
|
||||
}
|
||||
uint64_t ignored;
|
||||
return ParseFileName(basename, &ignored, type);
|
||||
}
|
||||
|
||||
// Notified when log reader encounters corruption.
|
||||
class CorruptionReporter : public log::Reader::Reporter {
|
||||
public:
|
||||
void Corruption(size_t bytes, const Status& status) override {
|
||||
std::string r = "corruption: ";
|
||||
AppendNumberTo(&r, bytes);
|
||||
r += " bytes; ";
|
||||
r += status.ToString();
|
||||
r.push_back('\n');
|
||||
dst_->Append(r);
|
||||
}
|
||||
|
||||
WritableFile* dst_;
|
||||
};
|
||||
|
||||
// Print contents of a log file. (*func)() is called on every record.
|
||||
Status PrintLogContents(Env* env, const std::string& fname,
|
||||
void (*func)(uint64_t, Slice, WritableFile*),
|
||||
WritableFile* dst) {
|
||||
SequentialFile* file;
|
||||
Status s = env->NewSequentialFile(fname, &file);
|
||||
if (!s.ok()) {
|
||||
return s;
|
||||
}
|
||||
CorruptionReporter reporter;
|
||||
reporter.dst_ = dst;
|
||||
log::Reader reader(file, &reporter, true, 0);
|
||||
Slice record;
|
||||
std::string scratch;
|
||||
while (reader.ReadRecord(&record, &scratch)) {
|
||||
(*func)(reader.LastRecordOffset(), record, dst);
|
||||
}
|
||||
delete file;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Called on every item found in a WriteBatch.
|
||||
class WriteBatchItemPrinter : public WriteBatch::Handler {
|
||||
public:
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
std::string r = " put '";
|
||||
AppendEscapedStringTo(&r, key);
|
||||
r += "' '";
|
||||
AppendEscapedStringTo(&r, value);
|
||||
r += "'\n";
|
||||
dst_->Append(r);
|
||||
}
|
||||
void Delete(const Slice& key) override {
|
||||
std::string r = " del '";
|
||||
AppendEscapedStringTo(&r, key);
|
||||
r += "'\n";
|
||||
dst_->Append(r);
|
||||
}
|
||||
|
||||
WritableFile* dst_;
|
||||
};
|
||||
|
||||
// Called on every log record (each one of which is a WriteBatch)
|
||||
// found in a kLogFile.
|
||||
static void WriteBatchPrinter(uint64_t pos, Slice record, WritableFile* dst) {
|
||||
std::string r = "--- offset ";
|
||||
AppendNumberTo(&r, pos);
|
||||
r += "; ";
|
||||
if (record.size() < 12) {
|
||||
r += "log record length ";
|
||||
AppendNumberTo(&r, record.size());
|
||||
r += " is too small\n";
|
||||
dst->Append(r);
|
||||
return;
|
||||
}
|
||||
WriteBatch batch;
|
||||
WriteBatchInternal::SetContents(&batch, record);
|
||||
r += "sequence ";
|
||||
AppendNumberTo(&r, WriteBatchInternal::Sequence(&batch));
|
||||
r.push_back('\n');
|
||||
dst->Append(r);
|
||||
WriteBatchItemPrinter batch_item_printer;
|
||||
batch_item_printer.dst_ = dst;
|
||||
Status s = batch.Iterate(&batch_item_printer);
|
||||
if (!s.ok()) {
|
||||
dst->Append(" error: " + s.ToString() + "\n");
|
||||
}
|
||||
}
|
||||
|
||||
Status DumpLog(Env* env, const std::string& fname, WritableFile* dst) {
|
||||
return PrintLogContents(env, fname, WriteBatchPrinter, dst);
|
||||
}
|
||||
|
||||
// Called on every log record (each one of which is a WriteBatch)
|
||||
// found in a kDescriptorFile.
|
||||
static void VersionEditPrinter(uint64_t pos, Slice record, WritableFile* dst) {
|
||||
std::string r = "--- offset ";
|
||||
AppendNumberTo(&r, pos);
|
||||
r += "; ";
|
||||
VersionEdit edit;
|
||||
Status s = edit.DecodeFrom(record);
|
||||
if (!s.ok()) {
|
||||
r += s.ToString();
|
||||
r.push_back('\n');
|
||||
} else {
|
||||
r += edit.DebugString();
|
||||
}
|
||||
dst->Append(r);
|
||||
}
|
||||
|
||||
Status DumpDescriptor(Env* env, const std::string& fname, WritableFile* dst) {
|
||||
return PrintLogContents(env, fname, VersionEditPrinter, dst);
|
||||
}
|
||||
|
||||
Status DumpTable(Env* env, const std::string& fname, WritableFile* dst) {
|
||||
uint64_t file_size;
|
||||
RandomAccessFile* file = nullptr;
|
||||
Table* table = nullptr;
|
||||
Status s = env->GetFileSize(fname, &file_size);
|
||||
if (s.ok()) {
|
||||
s = env->NewRandomAccessFile(fname, &file);
|
||||
}
|
||||
if (s.ok()) {
|
||||
// We use the default comparator, which may or may not match the
|
||||
// comparator used in this database. However this should not cause
|
||||
// problems since we only use Table operations that do not require
|
||||
// any comparisons. In particular, we do not call Seek or Prev.
|
||||
s = Table::Open(Options(), file, file_size, &table);
|
||||
}
|
||||
if (!s.ok()) {
|
||||
delete table;
|
||||
delete file;
|
||||
return s;
|
||||
}
|
||||
|
||||
ReadOptions ro;
|
||||
ro.fill_cache = false;
|
||||
Iterator* iter = table->NewIterator(ro);
|
||||
std::string r;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
r.clear();
|
||||
ParsedInternalKey key;
|
||||
if (!ParseInternalKey(iter->key(), &key)) {
|
||||
r = "badkey '";
|
||||
AppendEscapedStringTo(&r, iter->key());
|
||||
r += "' => '";
|
||||
AppendEscapedStringTo(&r, iter->value());
|
||||
r += "'\n";
|
||||
dst->Append(r);
|
||||
} else {
|
||||
r = "'";
|
||||
AppendEscapedStringTo(&r, key.user_key);
|
||||
r += "' @ ";
|
||||
AppendNumberTo(&r, key.sequence);
|
||||
r += " : ";
|
||||
if (key.type == kTypeDeletion) {
|
||||
r += "del";
|
||||
} else if (key.type == kTypeValue) {
|
||||
r += "val";
|
||||
} else {
|
||||
AppendNumberTo(&r, key.type);
|
||||
}
|
||||
r += " => '";
|
||||
AppendEscapedStringTo(&r, iter->value());
|
||||
r += "'\n";
|
||||
dst->Append(r);
|
||||
}
|
||||
}
|
||||
s = iter->status();
|
||||
if (!s.ok()) {
|
||||
dst->Append("iterator error: " + s.ToString() + "\n");
|
||||
}
|
||||
|
||||
delete iter;
|
||||
delete table;
|
||||
delete file;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Status DumpFile(Env* env, const std::string& fname, WritableFile* dst) {
|
||||
FileType ftype;
|
||||
if (!GuessType(fname, &ftype)) {
|
||||
return Status::InvalidArgument(fname + ": unknown file type");
|
||||
}
|
||||
switch (ftype) {
|
||||
case kLogFile:
|
||||
return DumpLog(env, fname, dst);
|
||||
case kDescriptorFile:
|
||||
return DumpDescriptor(env, fname, dst);
|
||||
case kTableFile:
|
||||
return DumpTable(env, fname, dst);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return Status::InvalidArgument(fname + ": not a dump-able file type");
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
108
3rdparty/leveldb/src/env.cc
vendored
Normal file
108
3rdparty/leveldb/src/env.cc
vendored
Normal file
@@ -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.
|
||||
|
||||
#include "leveldb/env.h"
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
// This workaround can be removed when leveldb::Env::DeleteFile is removed.
|
||||
// See env.h for justification.
|
||||
#if defined(_WIN32) && defined(LEVELDB_DELETEFILE_UNDEFINED)
|
||||
#undef DeleteFile
|
||||
#endif
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
Env::Env() = default;
|
||||
|
||||
Env::~Env() = default;
|
||||
|
||||
Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) {
|
||||
return Status::NotSupported("NewAppendableFile", fname);
|
||||
}
|
||||
|
||||
Status Env::RemoveDir(const std::string& dirname) { return DeleteDir(dirname); }
|
||||
Status Env::DeleteDir(const std::string& dirname) { return RemoveDir(dirname); }
|
||||
|
||||
Status Env::RemoveFile(const std::string& fname) { return DeleteFile(fname); }
|
||||
Status Env::DeleteFile(const std::string& fname) { return RemoveFile(fname); }
|
||||
|
||||
SequentialFile::~SequentialFile() = default;
|
||||
|
||||
RandomAccessFile::~RandomAccessFile() = default;
|
||||
|
||||
WritableFile::~WritableFile() = default;
|
||||
|
||||
Logger::~Logger() = default;
|
||||
|
||||
FileLock::~FileLock() = default;
|
||||
|
||||
void Log(Logger* info_log, const char* format, ...) {
|
||||
if (info_log != nullptr) {
|
||||
std::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->RemoveFile(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
|
||||
816
3rdparty/leveldb/src/env_windows.cc
vendored
Normal file
816
3rdparty/leveldb/src/env_windows.cc
vendored
Normal file
@@ -0,0 +1,816 @@
|
||||
// Copyright (c) 2018 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.
|
||||
|
||||
// Prevent Windows headers from defining min/max macros and instead
|
||||
// use STL.
|
||||
#ifndef NOMINMAX
|
||||
#define NOMINMAX
|
||||
#endif // ifndef NOMINMAX
|
||||
#include <windows.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/slice.h"
|
||||
#include "port/port.h"
|
||||
#include "port/thread_annotations.h"
|
||||
#include "util/env_windows_test_helper.h"
|
||||
#include "util/logging.h"
|
||||
#include "util/mutexlock.h"
|
||||
#include "util/windows_logger.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const size_t kWritableFileBufferSize = 65536;
|
||||
|
||||
// Up to 1000 mmaps for 64-bit binaries; none for 32-bit.
|
||||
constexpr int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0;
|
||||
|
||||
// Can be set by by EnvWindowsTestHelper::SetReadOnlyMMapLimit().
|
||||
int g_mmap_limit = kDefaultMmapLimit;
|
||||
|
||||
std::string GetWindowsErrorMessage(DWORD error_code) {
|
||||
std::string message;
|
||||
char* error_text = nullptr;
|
||||
// Use MBCS version of FormatMessage to match return value.
|
||||
size_t error_text_size = ::FormatMessageA(
|
||||
FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER |
|
||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||
nullptr, error_code, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||
reinterpret_cast<char*>(&error_text), 0, nullptr);
|
||||
if (!error_text) {
|
||||
return message;
|
||||
}
|
||||
message.assign(error_text, error_text_size);
|
||||
::LocalFree(error_text);
|
||||
return message;
|
||||
}
|
||||
|
||||
Status WindowsError(const std::string& context, DWORD error_code) {
|
||||
if (error_code == ERROR_FILE_NOT_FOUND || error_code == ERROR_PATH_NOT_FOUND)
|
||||
return Status::NotFound(context, GetWindowsErrorMessage(error_code));
|
||||
return Status::IOError(context, GetWindowsErrorMessage(error_code));
|
||||
}
|
||||
|
||||
class ScopedHandle {
|
||||
public:
|
||||
ScopedHandle(HANDLE handle) : handle_(handle) {}
|
||||
ScopedHandle(const ScopedHandle&) = delete;
|
||||
ScopedHandle(ScopedHandle&& other) noexcept : handle_(other.Release()) {}
|
||||
~ScopedHandle() { Close(); }
|
||||
|
||||
ScopedHandle& operator=(const ScopedHandle&) = delete;
|
||||
|
||||
ScopedHandle& operator=(ScopedHandle&& rhs) noexcept {
|
||||
if (this != &rhs) handle_ = rhs.Release();
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool Close() {
|
||||
if (!is_valid()) {
|
||||
return true;
|
||||
}
|
||||
HANDLE h = handle_;
|
||||
handle_ = INVALID_HANDLE_VALUE;
|
||||
return ::CloseHandle(h);
|
||||
}
|
||||
|
||||
bool is_valid() const {
|
||||
return handle_ != INVALID_HANDLE_VALUE && handle_ != nullptr;
|
||||
}
|
||||
|
||||
HANDLE get() const { return handle_; }
|
||||
|
||||
HANDLE Release() {
|
||||
HANDLE h = handle_;
|
||||
handle_ = INVALID_HANDLE_VALUE;
|
||||
return h;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE handle_;
|
||||
};
|
||||
|
||||
// Helper class to limit resource usage to avoid exhaustion.
|
||||
// Currently used to limit read-only file descriptors and mmap file usage
|
||||
// so that we do not run out of file descriptors or virtual memory, or run into
|
||||
// kernel performance problems for very large databases.
|
||||
class Limiter {
|
||||
public:
|
||||
// Limit maximum number of resources to |max_acquires|.
|
||||
Limiter(int max_acquires)
|
||||
:
|
||||
#if !defined(NDEBUG)
|
||||
max_acquires_(max_acquires),
|
||||
#endif // !defined(NDEBUG)
|
||||
acquires_allowed_(max_acquires) {
|
||||
assert(max_acquires >= 0);
|
||||
}
|
||||
|
||||
Limiter(const Limiter&) = delete;
|
||||
Limiter operator=(const Limiter&) = delete;
|
||||
|
||||
// If another resource is available, acquire it and return true.
|
||||
// Else return false.
|
||||
bool Acquire() {
|
||||
int old_acquires_allowed =
|
||||
acquires_allowed_.fetch_sub(1, std::memory_order_relaxed);
|
||||
|
||||
if (old_acquires_allowed > 0) return true;
|
||||
|
||||
acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Release a resource acquired by a previous call to Acquire() that returned
|
||||
// true.
|
||||
void Release() {
|
||||
int old_acquires_allowed =
|
||||
acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
|
||||
|
||||
// Silence compiler warnings about unused arguments when NDEBUG is defined.
|
||||
(void)old_acquires_allowed;
|
||||
// If the check below fails, Release() was called more times than acquire.
|
||||
assert(old_acquires_allowed < max_acquires_);
|
||||
}
|
||||
|
||||
private:
|
||||
#if !defined(NDEBUG)
|
||||
// Catches an excessive number of Release() calls.
|
||||
const int max_acquires_;
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
// The number of available resources.
|
||||
//
|
||||
// This is a counter and is not tied to the invariants of any other class, so
|
||||
// it can be operated on safely using std::memory_order_relaxed.
|
||||
std::atomic<int> acquires_allowed_;
|
||||
};
|
||||
|
||||
class WindowsSequentialFile : public SequentialFile {
|
||||
public:
|
||||
WindowsSequentialFile(std::string filename, ScopedHandle handle)
|
||||
: handle_(std::move(handle)), filename_(std::move(filename)) {}
|
||||
~WindowsSequentialFile() override {}
|
||||
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
DWORD bytes_read;
|
||||
// DWORD is 32-bit, but size_t could technically be larger. However leveldb
|
||||
// files are limited to leveldb::Options::max_file_size which is clamped to
|
||||
// 1<<30 or 1 GiB.
|
||||
assert(n <= std::numeric_limits<DWORD>::max());
|
||||
if (!::ReadFile(handle_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
|
||||
nullptr)) {
|
||||
return WindowsError(filename_, ::GetLastError());
|
||||
}
|
||||
|
||||
*result = Slice(scratch, bytes_read);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status Skip(uint64_t n) override {
|
||||
LARGE_INTEGER distance;
|
||||
distance.QuadPart = n;
|
||||
if (!::SetFilePointerEx(handle_.get(), distance, nullptr, FILE_CURRENT)) {
|
||||
return WindowsError(filename_, ::GetLastError());
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
const ScopedHandle handle_;
|
||||
const std::string filename_;
|
||||
};
|
||||
|
||||
class WindowsRandomAccessFile : public RandomAccessFile {
|
||||
public:
|
||||
WindowsRandomAccessFile(std::string filename, ScopedHandle handle)
|
||||
: handle_(std::move(handle)), filename_(std::move(filename)) {}
|
||||
|
||||
~WindowsRandomAccessFile() override = default;
|
||||
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
DWORD bytes_read = 0;
|
||||
OVERLAPPED overlapped = {0};
|
||||
|
||||
overlapped.OffsetHigh = static_cast<DWORD>(offset >> 32);
|
||||
overlapped.Offset = static_cast<DWORD>(offset);
|
||||
if (!::ReadFile(handle_.get(), scratch, static_cast<DWORD>(n), &bytes_read,
|
||||
&overlapped)) {
|
||||
DWORD error_code = ::GetLastError();
|
||||
if (error_code != ERROR_HANDLE_EOF) {
|
||||
*result = Slice(scratch, 0);
|
||||
return Status::IOError(filename_, GetWindowsErrorMessage(error_code));
|
||||
}
|
||||
}
|
||||
|
||||
*result = Slice(scratch, bytes_read);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
const ScopedHandle handle_;
|
||||
const std::string filename_;
|
||||
};
|
||||
|
||||
class WindowsMmapReadableFile : public RandomAccessFile {
|
||||
public:
|
||||
// base[0,length-1] contains the mmapped contents of the file.
|
||||
WindowsMmapReadableFile(std::string filename, char* mmap_base, size_t length,
|
||||
Limiter* mmap_limiter)
|
||||
: mmap_base_(mmap_base),
|
||||
length_(length),
|
||||
mmap_limiter_(mmap_limiter),
|
||||
filename_(std::move(filename)) {}
|
||||
|
||||
~WindowsMmapReadableFile() override {
|
||||
::UnmapViewOfFile(mmap_base_);
|
||||
mmap_limiter_->Release();
|
||||
}
|
||||
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
if (offset + n > length_) {
|
||||
*result = Slice();
|
||||
return WindowsError(filename_, ERROR_INVALID_PARAMETER);
|
||||
}
|
||||
|
||||
*result = Slice(mmap_base_ + offset, n);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
char* const mmap_base_;
|
||||
const size_t length_;
|
||||
Limiter* const mmap_limiter_;
|
||||
const std::string filename_;
|
||||
};
|
||||
|
||||
class WindowsWritableFile : public WritableFile {
|
||||
public:
|
||||
WindowsWritableFile(std::string filename, ScopedHandle handle)
|
||||
: pos_(0), handle_(std::move(handle)), filename_(std::move(filename)) {}
|
||||
|
||||
~WindowsWritableFile() override = default;
|
||||
|
||||
Status Append(const Slice& data) override {
|
||||
size_t write_size = data.size();
|
||||
const char* write_data = data.data();
|
||||
|
||||
// Fit as much as possible into buffer.
|
||||
size_t copy_size = std::min(write_size, kWritableFileBufferSize - pos_);
|
||||
std::memcpy(buf_ + pos_, write_data, copy_size);
|
||||
write_data += copy_size;
|
||||
write_size -= copy_size;
|
||||
pos_ += copy_size;
|
||||
if (write_size == 0) {
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// Can't fit in buffer, so need to do at least one write.
|
||||
Status status = FlushBuffer();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
// Small writes go to buffer, large writes are written directly.
|
||||
if (write_size < kWritableFileBufferSize) {
|
||||
std::memcpy(buf_, write_data, write_size);
|
||||
pos_ = write_size;
|
||||
return Status::OK();
|
||||
}
|
||||
return WriteUnbuffered(write_data, write_size);
|
||||
}
|
||||
|
||||
Status Close() override {
|
||||
Status status = FlushBuffer();
|
||||
if (!handle_.Close() && status.ok()) {
|
||||
status = WindowsError(filename_, ::GetLastError());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status Flush() override { return FlushBuffer(); }
|
||||
|
||||
Status Sync() override {
|
||||
// On Windows no need to sync parent directory. Its metadata will be updated
|
||||
// via the creation of the new file, without an explicit sync.
|
||||
|
||||
Status status = FlushBuffer();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if (!::FlushFileBuffers(handle_.get())) {
|
||||
return Status::IOError(filename_,
|
||||
GetWindowsErrorMessage(::GetLastError()));
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
Status FlushBuffer() {
|
||||
Status status = WriteUnbuffered(buf_, pos_);
|
||||
pos_ = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
Status WriteUnbuffered(const char* data, size_t size) {
|
||||
DWORD bytes_written;
|
||||
if (!::WriteFile(handle_.get(), data, static_cast<DWORD>(size),
|
||||
&bytes_written, nullptr)) {
|
||||
return Status::IOError(filename_,
|
||||
GetWindowsErrorMessage(::GetLastError()));
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
// buf_[0, pos_-1] contains data to be written to handle_.
|
||||
char buf_[kWritableFileBufferSize];
|
||||
size_t pos_;
|
||||
|
||||
ScopedHandle handle_;
|
||||
const std::string filename_;
|
||||
};
|
||||
|
||||
// Lock or unlock the entire file as specified by |lock|. Returns true
|
||||
// when successful, false upon failure. Caller should call ::GetLastError()
|
||||
// to determine cause of failure
|
||||
bool LockOrUnlock(HANDLE handle, bool lock) {
|
||||
if (lock) {
|
||||
return ::LockFile(handle,
|
||||
/*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0,
|
||||
/*nNumberOfBytesToLockLow=*/MAXDWORD,
|
||||
/*nNumberOfBytesToLockHigh=*/MAXDWORD);
|
||||
} else {
|
||||
return ::UnlockFile(handle,
|
||||
/*dwFileOffsetLow=*/0, /*dwFileOffsetHigh=*/0,
|
||||
/*nNumberOfBytesToLockLow=*/MAXDWORD,
|
||||
/*nNumberOfBytesToLockHigh=*/MAXDWORD);
|
||||
}
|
||||
}
|
||||
|
||||
class WindowsFileLock : public FileLock {
|
||||
public:
|
||||
WindowsFileLock(ScopedHandle handle, std::string filename)
|
||||
: handle_(std::move(handle)), filename_(std::move(filename)) {}
|
||||
|
||||
const ScopedHandle& handle() const { return handle_; }
|
||||
const std::string& filename() const { return filename_; }
|
||||
|
||||
private:
|
||||
const ScopedHandle handle_;
|
||||
const std::string filename_;
|
||||
};
|
||||
|
||||
class WindowsEnv : public Env {
|
||||
public:
|
||||
WindowsEnv();
|
||||
~WindowsEnv() override {
|
||||
static const char msg[] =
|
||||
"WindowsEnv singleton destroyed. Unsupported behavior!\n";
|
||||
std::fwrite(msg, 1, sizeof(msg), stderr);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
Status NewSequentialFile(const std::string& filename,
|
||||
SequentialFile** result) override {
|
||||
*result = nullptr;
|
||||
DWORD desired_access = GENERIC_READ;
|
||||
DWORD share_mode = FILE_SHARE_READ;
|
||||
ScopedHandle handle = ::CreateFileA(
|
||||
filename.c_str(), desired_access, share_mode,
|
||||
/*lpSecurityAttributes=*/nullptr, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
|
||||
/*hTemplateFile=*/nullptr);
|
||||
if (!handle.is_valid()) {
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
}
|
||||
|
||||
*result = new WindowsSequentialFile(filename, std::move(handle));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewRandomAccessFile(const std::string& filename,
|
||||
RandomAccessFile** result) override {
|
||||
*result = nullptr;
|
||||
DWORD desired_access = GENERIC_READ;
|
||||
DWORD share_mode = FILE_SHARE_READ;
|
||||
ScopedHandle handle =
|
||||
::CreateFileA(filename.c_str(), desired_access, share_mode,
|
||||
/*lpSecurityAttributes=*/nullptr, OPEN_EXISTING,
|
||||
FILE_ATTRIBUTE_READONLY,
|
||||
/*hTemplateFile=*/nullptr);
|
||||
if (!handle.is_valid()) {
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
}
|
||||
if (!mmap_limiter_.Acquire()) {
|
||||
*result = new WindowsRandomAccessFile(filename, std::move(handle));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
LARGE_INTEGER file_size;
|
||||
Status status;
|
||||
if (!::GetFileSizeEx(handle.get(), &file_size)) {
|
||||
mmap_limiter_.Release();
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
}
|
||||
|
||||
ScopedHandle mapping =
|
||||
::CreateFileMappingA(handle.get(),
|
||||
/*security attributes=*/nullptr, PAGE_READONLY,
|
||||
/*dwMaximumSizeHigh=*/0,
|
||||
/*dwMaximumSizeLow=*/0,
|
||||
/*lpName=*/nullptr);
|
||||
if (mapping.is_valid()) {
|
||||
void* mmap_base = ::MapViewOfFile(mapping.get(), FILE_MAP_READ,
|
||||
/*dwFileOffsetHigh=*/0,
|
||||
/*dwFileOffsetLow=*/0,
|
||||
/*dwNumberOfBytesToMap=*/0);
|
||||
if (mmap_base) {
|
||||
*result = new WindowsMmapReadableFile(
|
||||
filename, reinterpret_cast<char*>(mmap_base),
|
||||
static_cast<size_t>(file_size.QuadPart), &mmap_limiter_);
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
mmap_limiter_.Release();
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
}
|
||||
|
||||
Status NewWritableFile(const std::string& filename,
|
||||
WritableFile** result) override {
|
||||
DWORD desired_access = GENERIC_WRITE;
|
||||
DWORD share_mode = 0; // Exclusive access.
|
||||
ScopedHandle handle = ::CreateFileA(
|
||||
filename.c_str(), desired_access, share_mode,
|
||||
/*lpSecurityAttributes=*/nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
||||
/*hTemplateFile=*/nullptr);
|
||||
if (!handle.is_valid()) {
|
||||
*result = nullptr;
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
}
|
||||
|
||||
*result = new WindowsWritableFile(filename, std::move(handle));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewAppendableFile(const std::string& filename,
|
||||
WritableFile** result) override {
|
||||
DWORD desired_access = FILE_APPEND_DATA;
|
||||
DWORD share_mode = 0; // Exclusive access.
|
||||
ScopedHandle handle = ::CreateFileA(
|
||||
filename.c_str(), desired_access, share_mode,
|
||||
/*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
||||
/*hTemplateFile=*/nullptr);
|
||||
if (!handle.is_valid()) {
|
||||
*result = nullptr;
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
}
|
||||
|
||||
*result = new WindowsWritableFile(filename, std::move(handle));
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool FileExists(const std::string& filename) override {
|
||||
return GetFileAttributesA(filename.c_str()) != INVALID_FILE_ATTRIBUTES;
|
||||
}
|
||||
|
||||
Status GetChildren(const std::string& directory_path,
|
||||
std::vector<std::string>* result) override {
|
||||
const std::string find_pattern = directory_path + "\\*";
|
||||
WIN32_FIND_DATAA find_data;
|
||||
HANDLE dir_handle = ::FindFirstFileA(find_pattern.c_str(), &find_data);
|
||||
if (dir_handle == INVALID_HANDLE_VALUE) {
|
||||
DWORD last_error = ::GetLastError();
|
||||
if (last_error == ERROR_FILE_NOT_FOUND) {
|
||||
return Status::OK();
|
||||
}
|
||||
return WindowsError(directory_path, last_error);
|
||||
}
|
||||
do {
|
||||
char base_name[_MAX_FNAME];
|
||||
char ext[_MAX_EXT];
|
||||
|
||||
if (!_splitpath_s(find_data.cFileName, nullptr, 0, nullptr, 0, base_name,
|
||||
ARRAYSIZE(base_name), ext, ARRAYSIZE(ext))) {
|
||||
result->emplace_back(std::string(base_name) + ext);
|
||||
}
|
||||
} while (::FindNextFileA(dir_handle, &find_data));
|
||||
DWORD last_error = ::GetLastError();
|
||||
::FindClose(dir_handle);
|
||||
if (last_error != ERROR_NO_MORE_FILES) {
|
||||
return WindowsError(directory_path, last_error);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status RemoveFile(const std::string& filename) override {
|
||||
if (!::DeleteFileA(filename.c_str())) {
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CreateDir(const std::string& dirname) override {
|
||||
if (!::CreateDirectoryA(dirname.c_str(), nullptr)) {
|
||||
return WindowsError(dirname, ::GetLastError());
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status RemoveDir(const std::string& dirname) override {
|
||||
if (!::RemoveDirectoryA(dirname.c_str())) {
|
||||
return WindowsError(dirname, ::GetLastError());
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetFileSize(const std::string& filename, uint64_t* size) override {
|
||||
WIN32_FILE_ATTRIBUTE_DATA file_attributes;
|
||||
if (!::GetFileAttributesExA(filename.c_str(), GetFileExInfoStandard,
|
||||
&file_attributes)) {
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
}
|
||||
ULARGE_INTEGER file_size;
|
||||
file_size.HighPart = file_attributes.nFileSizeHigh;
|
||||
file_size.LowPart = file_attributes.nFileSizeLow;
|
||||
*size = file_size.QuadPart;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status RenameFile(const std::string& from, const std::string& to) override {
|
||||
// Try a simple move first. It will only succeed when |to| doesn't already
|
||||
// exist.
|
||||
if (::MoveFileA(from.c_str(), to.c_str())) {
|
||||
return Status::OK();
|
||||
}
|
||||
DWORD move_error = ::GetLastError();
|
||||
|
||||
// Try the full-blown replace if the move fails, as ReplaceFile will only
|
||||
// succeed when |to| does exist. When writing to a network share, we may not
|
||||
// be able to change the ACLs. Ignore ACL errors then
|
||||
// (REPLACEFILE_IGNORE_MERGE_ERRORS).
|
||||
if (::ReplaceFileA(to.c_str(), from.c_str(), /*lpBackupFileName=*/nullptr,
|
||||
REPLACEFILE_IGNORE_MERGE_ERRORS,
|
||||
/*lpExclude=*/nullptr, /*lpReserved=*/nullptr)) {
|
||||
return Status::OK();
|
||||
}
|
||||
DWORD replace_error = ::GetLastError();
|
||||
// In the case of FILE_ERROR_NOT_FOUND from ReplaceFile, it is likely that
|
||||
// |to| does not exist. In this case, the more relevant error comes from the
|
||||
// call to MoveFile.
|
||||
if (replace_error == ERROR_FILE_NOT_FOUND ||
|
||||
replace_error == ERROR_PATH_NOT_FOUND) {
|
||||
return WindowsError(from, move_error);
|
||||
} else {
|
||||
return WindowsError(from, replace_error);
|
||||
}
|
||||
}
|
||||
|
||||
Status LockFile(const std::string& filename, FileLock** lock) override {
|
||||
*lock = nullptr;
|
||||
Status result;
|
||||
ScopedHandle handle = ::CreateFileA(
|
||||
filename.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ,
|
||||
/*lpSecurityAttributes=*/nullptr, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL,
|
||||
nullptr);
|
||||
if (!handle.is_valid()) {
|
||||
result = WindowsError(filename, ::GetLastError());
|
||||
} else if (!LockOrUnlock(handle.get(), true)) {
|
||||
result = WindowsError("lock " + filename, ::GetLastError());
|
||||
} else {
|
||||
*lock = new WindowsFileLock(std::move(handle), filename);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Status UnlockFile(FileLock* lock) override {
|
||||
WindowsFileLock* windows_file_lock =
|
||||
reinterpret_cast<WindowsFileLock*>(lock);
|
||||
if (!LockOrUnlock(windows_file_lock->handle().get(), false)) {
|
||||
return WindowsError("unlock " + windows_file_lock->filename(),
|
||||
::GetLastError());
|
||||
}
|
||||
delete windows_file_lock;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
void Schedule(void (*background_work_function)(void* background_work_arg),
|
||||
void* background_work_arg) override;
|
||||
|
||||
void StartThread(void (*thread_main)(void* thread_main_arg),
|
||||
void* thread_main_arg) override {
|
||||
std::thread new_thread(thread_main, thread_main_arg);
|
||||
new_thread.detach();
|
||||
}
|
||||
|
||||
Status GetTestDirectory(std::string* result) override {
|
||||
const char* env = getenv("TEST_TMPDIR");
|
||||
if (env && env[0] != '\0') {
|
||||
*result = env;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
char tmp_path[MAX_PATH];
|
||||
if (!GetTempPathA(ARRAYSIZE(tmp_path), tmp_path)) {
|
||||
return WindowsError("GetTempPath", ::GetLastError());
|
||||
}
|
||||
std::stringstream ss;
|
||||
ss << tmp_path << "leveldbtest-" << std::this_thread::get_id();
|
||||
*result = ss.str();
|
||||
|
||||
// Directory may already exist
|
||||
CreateDir(*result);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewLogger(const std::string& filename, Logger** result) override {
|
||||
std::FILE* fp = std::fopen(filename.c_str(), "wN");
|
||||
if (fp == nullptr) {
|
||||
*result = nullptr;
|
||||
return WindowsError(filename, ::GetLastError());
|
||||
} else {
|
||||
*result = new WindowsLogger(fp);
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t NowMicros() override {
|
||||
// GetSystemTimeAsFileTime typically has a resolution of 10-20 msec.
|
||||
// TODO(cmumford): Switch to GetSystemTimePreciseAsFileTime which is
|
||||
// available in Windows 8 and later.
|
||||
FILETIME ft;
|
||||
::GetSystemTimeAsFileTime(&ft);
|
||||
// Each tick represents a 100-nanosecond intervals since January 1, 1601
|
||||
// (UTC).
|
||||
uint64_t num_ticks =
|
||||
(static_cast<uint64_t>(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
|
||||
return num_ticks / 10;
|
||||
}
|
||||
|
||||
void SleepForMicroseconds(int micros) override {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||
}
|
||||
|
||||
private:
|
||||
void BackgroundThreadMain();
|
||||
|
||||
static void BackgroundThreadEntryPoint(WindowsEnv* env) {
|
||||
env->BackgroundThreadMain();
|
||||
}
|
||||
|
||||
// Stores the work item data in a Schedule() call.
|
||||
//
|
||||
// Instances are constructed on the thread calling Schedule() and used on the
|
||||
// background thread.
|
||||
//
|
||||
// This structure is thread-safe because it is immutable.
|
||||
struct BackgroundWorkItem {
|
||||
explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
|
||||
: function(function), arg(arg) {}
|
||||
|
||||
void (*const function)(void*);
|
||||
void* const arg;
|
||||
};
|
||||
|
||||
port::Mutex background_work_mutex_;
|
||||
port::CondVar background_work_cv_ GUARDED_BY(background_work_mutex_);
|
||||
bool started_background_thread_ GUARDED_BY(background_work_mutex_);
|
||||
|
||||
std::queue<BackgroundWorkItem> background_work_queue_
|
||||
GUARDED_BY(background_work_mutex_);
|
||||
|
||||
Limiter mmap_limiter_; // Thread-safe.
|
||||
};
|
||||
|
||||
// Return the maximum number of concurrent mmaps.
|
||||
int MaxMmaps() { return g_mmap_limit; }
|
||||
|
||||
WindowsEnv::WindowsEnv()
|
||||
: background_work_cv_(&background_work_mutex_),
|
||||
started_background_thread_(false),
|
||||
mmap_limiter_(MaxMmaps()) {}
|
||||
|
||||
void WindowsEnv::Schedule(
|
||||
void (*background_work_function)(void* background_work_arg),
|
||||
void* background_work_arg) {
|
||||
background_work_mutex_.Lock();
|
||||
|
||||
// Start the background thread, if we haven't done so already.
|
||||
if (!started_background_thread_) {
|
||||
started_background_thread_ = true;
|
||||
std::thread background_thread(WindowsEnv::BackgroundThreadEntryPoint, this);
|
||||
background_thread.detach();
|
||||
}
|
||||
|
||||
// If the queue is empty, the background thread may be waiting for work.
|
||||
if (background_work_queue_.empty()) {
|
||||
background_work_cv_.Signal();
|
||||
}
|
||||
|
||||
background_work_queue_.emplace(background_work_function, background_work_arg);
|
||||
background_work_mutex_.Unlock();
|
||||
}
|
||||
|
||||
void WindowsEnv::BackgroundThreadMain() {
|
||||
while (true) {
|
||||
background_work_mutex_.Lock();
|
||||
|
||||
// Wait until there is work to be done.
|
||||
while (background_work_queue_.empty()) {
|
||||
background_work_cv_.Wait();
|
||||
}
|
||||
|
||||
assert(!background_work_queue_.empty());
|
||||
auto background_work_function = background_work_queue_.front().function;
|
||||
void* background_work_arg = background_work_queue_.front().arg;
|
||||
background_work_queue_.pop();
|
||||
|
||||
background_work_mutex_.Unlock();
|
||||
background_work_function(background_work_arg);
|
||||
}
|
||||
}
|
||||
|
||||
// Wraps an Env instance whose destructor is never created.
|
||||
//
|
||||
// Intended usage:
|
||||
// using PlatformSingletonEnv = SingletonEnv<PlatformEnv>;
|
||||
// void ConfigurePosixEnv(int param) {
|
||||
// PlatformSingletonEnv::AssertEnvNotInitialized();
|
||||
// // set global configuration flags.
|
||||
// }
|
||||
// Env* Env::Default() {
|
||||
// static PlatformSingletonEnv default_env;
|
||||
// return default_env.env();
|
||||
// }
|
||||
template <typename EnvType>
|
||||
class SingletonEnv {
|
||||
public:
|
||||
SingletonEnv() {
|
||||
#if !defined(NDEBUG)
|
||||
env_initialized_.store(true, std::memory_order_relaxed);
|
||||
#endif // !defined(NDEBUG)
|
||||
static_assert(sizeof(env_storage_) >= sizeof(EnvType),
|
||||
"env_storage_ will not fit the Env");
|
||||
static_assert(alignof(decltype(env_storage_)) >= alignof(EnvType),
|
||||
"env_storage_ does not meet the Env's alignment needs");
|
||||
new (&env_storage_) EnvType();
|
||||
}
|
||||
~SingletonEnv() = default;
|
||||
|
||||
SingletonEnv(const SingletonEnv&) = delete;
|
||||
SingletonEnv& operator=(const SingletonEnv&) = delete;
|
||||
|
||||
Env* env() { return reinterpret_cast<Env*>(&env_storage_); }
|
||||
|
||||
static void AssertEnvNotInitialized() {
|
||||
#if !defined(NDEBUG)
|
||||
assert(!env_initialized_.load(std::memory_order_relaxed));
|
||||
#endif // !defined(NDEBUG)
|
||||
}
|
||||
|
||||
private:
|
||||
typename std::aligned_storage<sizeof(EnvType), alignof(EnvType)>::type
|
||||
env_storage_;
|
||||
#if !defined(NDEBUG)
|
||||
static std::atomic<bool> env_initialized_;
|
||||
#endif // !defined(NDEBUG)
|
||||
};
|
||||
|
||||
#if !defined(NDEBUG)
|
||||
template <typename EnvType>
|
||||
std::atomic<bool> SingletonEnv<EnvType>::env_initialized_;
|
||||
#endif // !defined(NDEBUG)
|
||||
|
||||
using WindowsDefaultEnv = SingletonEnv<WindowsEnv>;
|
||||
|
||||
} // namespace
|
||||
|
||||
void EnvWindowsTestHelper::SetReadOnlyMMapLimit(int limit) {
|
||||
WindowsDefaultEnv::AssertEnvNotInitialized();
|
||||
g_mmap_limit = limit;
|
||||
}
|
||||
|
||||
Env* Env::Default() {
|
||||
static WindowsDefaultEnv env_container;
|
||||
return env_container.env();
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
141
3rdparty/leveldb/src/filename.cc
vendored
Normal file
141
3rdparty/leveldb/src/filename.cc
vendored
Normal file
@@ -0,0 +1,141 @@
|
||||
// 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 <cassert>
|
||||
#include <cstdio>
|
||||
|
||||
#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.
|
||||
Status WriteStringToFileSync(Env* env, const Slice& data,
|
||||
const std::string& fname);
|
||||
|
||||
static std::string MakeFileName(const std::string& dbname, uint64_t number,
|
||||
const char* suffix) {
|
||||
char buf[100];
|
||||
std::snprintf(buf, sizeof(buf), "/%06llu.%s",
|
||||
static_cast<unsigned long long>(number), suffix);
|
||||
return dbname + buf;
|
||||
}
|
||||
|
||||
std::string LogFileName(const std::string& dbname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(dbname, number, "log");
|
||||
}
|
||||
|
||||
std::string TableFileName(const std::string& dbname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(dbname, number, "ldb");
|
||||
}
|
||||
|
||||
std::string SSTTableFileName(const std::string& dbname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
return MakeFileName(dbname, number, "sst");
|
||||
}
|
||||
|
||||
std::string DescriptorFileName(const std::string& dbname, uint64_t number) {
|
||||
assert(number > 0);
|
||||
char buf[100];
|
||||
std::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|ldb)
|
||||
bool ParseFileName(const std::string& filename, uint64_t* number,
|
||||
FileType* type) {
|
||||
Slice rest(filename);
|
||||
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") || suffix == Slice(".ldb")) {
|
||||
*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->RemoveFile(tmp);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
106
3rdparty/leveldb/src/filter_block.cc
vendored
Normal file
106
3rdparty/leveldb/src/filter_block.cc
vendored
Normal file
@@ -0,0 +1,106 @@
|
||||
// Copyright (c) 2012 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/filter_block.h"
|
||||
|
||||
#include "leveldb/filter_policy.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
// See doc/table_format.md for an explanation of the filter block format.
|
||||
|
||||
// Generate new filter every 2KB of data
|
||||
static const size_t kFilterBaseLg = 11;
|
||||
static const size_t kFilterBase = 1 << kFilterBaseLg;
|
||||
|
||||
FilterBlockBuilder::FilterBlockBuilder(const FilterPolicy* policy)
|
||||
: policy_(policy) {}
|
||||
|
||||
void FilterBlockBuilder::StartBlock(uint64_t block_offset) {
|
||||
uint64_t filter_index = (block_offset / kFilterBase);
|
||||
assert(filter_index >= filter_offsets_.size());
|
||||
while (filter_index > filter_offsets_.size()) {
|
||||
GenerateFilter();
|
||||
}
|
||||
}
|
||||
|
||||
void FilterBlockBuilder::AddKey(const Slice& key) {
|
||||
Slice k = key;
|
||||
start_.push_back(keys_.size());
|
||||
keys_.append(k.data(), k.size());
|
||||
}
|
||||
|
||||
Slice FilterBlockBuilder::Finish() {
|
||||
if (!start_.empty()) {
|
||||
GenerateFilter();
|
||||
}
|
||||
|
||||
// Append array of per-filter offsets
|
||||
const uint32_t array_offset = result_.size();
|
||||
for (size_t i = 0; i < filter_offsets_.size(); i++) {
|
||||
PutFixed32(&result_, filter_offsets_[i]);
|
||||
}
|
||||
|
||||
PutFixed32(&result_, array_offset);
|
||||
result_.push_back(kFilterBaseLg); // Save encoding parameter in result
|
||||
return Slice(result_);
|
||||
}
|
||||
|
||||
void FilterBlockBuilder::GenerateFilter() {
|
||||
const size_t num_keys = start_.size();
|
||||
if (num_keys == 0) {
|
||||
// Fast path if there are no keys for this filter
|
||||
filter_offsets_.push_back(result_.size());
|
||||
return;
|
||||
}
|
||||
|
||||
// Make list of keys from flattened key structure
|
||||
start_.push_back(keys_.size()); // Simplify length computation
|
||||
tmp_keys_.resize(num_keys);
|
||||
for (size_t i = 0; i < num_keys; i++) {
|
||||
const char* base = keys_.data() + start_[i];
|
||||
size_t length = start_[i + 1] - start_[i];
|
||||
tmp_keys_[i] = Slice(base, length);
|
||||
}
|
||||
|
||||
// Generate filter for current set of keys and append to result_.
|
||||
filter_offsets_.push_back(result_.size());
|
||||
policy_->CreateFilter(&tmp_keys_[0], static_cast<int>(num_keys), &result_);
|
||||
|
||||
tmp_keys_.clear();
|
||||
keys_.clear();
|
||||
start_.clear();
|
||||
}
|
||||
|
||||
FilterBlockReader::FilterBlockReader(const FilterPolicy* policy,
|
||||
const Slice& contents)
|
||||
: policy_(policy), data_(nullptr), offset_(nullptr), num_(0), base_lg_(0) {
|
||||
size_t n = contents.size();
|
||||
if (n < 5) return; // 1 byte for base_lg_ and 4 for start of offset array
|
||||
base_lg_ = contents[n - 1];
|
||||
uint32_t last_word = DecodeFixed32(contents.data() + n - 5);
|
||||
if (last_word > n - 5) return;
|
||||
data_ = contents.data();
|
||||
offset_ = data_ + last_word;
|
||||
num_ = (n - 5 - last_word) / 4;
|
||||
}
|
||||
|
||||
bool FilterBlockReader::KeyMayMatch(uint64_t block_offset, const Slice& key) {
|
||||
uint64_t index = block_offset >> base_lg_;
|
||||
if (index < num_) {
|
||||
uint32_t start = DecodeFixed32(offset_ + index * 4);
|
||||
uint32_t limit = DecodeFixed32(offset_ + index * 4 + 4);
|
||||
if (start <= limit && limit <= static_cast<size_t>(offset_ - data_)) {
|
||||
Slice filter = Slice(data_ + start, limit - start);
|
||||
return policy_->KeyMayMatch(key, filter);
|
||||
} else if (start == limit) {
|
||||
// Empty filters do not match any keys
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true; // Errors are treated as potential matches
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
11
3rdparty/leveldb/src/filter_policy.cc
vendored
Normal file
11
3rdparty/leveldb/src/filter_policy.cc
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Copyright (c) 2012 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/filter_policy.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
FilterPolicy::~FilterPolicy() {}
|
||||
|
||||
} // namespace leveldb
|
||||
164
3rdparty/leveldb/src/format.cc
vendored
Normal file
164
3rdparty/leveldb/src/format.cc
vendored
Normal file
@@ -0,0 +1,164 @@
|
||||
// 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 "leveldb/options.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 {
|
||||
const size_t original_size = dst->size();
|
||||
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);
|
||||
(void)original_size; // Disable unused variable warning.
|
||||
}
|
||||
|
||||
Status Footer::DecodeFrom(Slice* input) {
|
||||
if (input->size() < kEncodedLength) {
|
||||
return Status::Corruption("not an sstable (footer too short)");
|
||||
}
|
||||
|
||||
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::Corruption("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, BlockContents* result) {
|
||||
result->data = Slice();
|
||||
result->cachable = false;
|
||||
result->heap_allocated = false;
|
||||
|
||||
// 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.
|
||||
// Use it directly under the assumption that it will be live
|
||||
// while the file is open.
|
||||
delete[] buf;
|
||||
result->data = Slice(data, n);
|
||||
result->heap_allocated = false;
|
||||
result->cachable = false; // Do not double-cache
|
||||
} else {
|
||||
result->data = Slice(buf, n);
|
||||
result->heap_allocated = true;
|
||||
result->cachable = true;
|
||||
}
|
||||
|
||||
// Ok
|
||||
break;
|
||||
case kSnappyCompression: {
|
||||
size_t ulength = 0;
|
||||
if (!port::Snappy_GetUncompressedLength(data, n, &ulength)) {
|
||||
delete[] buf;
|
||||
return Status::Corruption("corrupted snappy compressed block length");
|
||||
}
|
||||
char* ubuf = new char[ulength];
|
||||
if (!port::Snappy_Uncompress(data, n, ubuf)) {
|
||||
delete[] buf;
|
||||
delete[] ubuf;
|
||||
return Status::Corruption("corrupted snappy compressed block contents");
|
||||
}
|
||||
delete[] buf;
|
||||
result->data = Slice(ubuf, ulength);
|
||||
result->heap_allocated = true;
|
||||
result->cachable = true;
|
||||
break;
|
||||
}
|
||||
case kZstdCompression: {
|
||||
size_t ulength = 0;
|
||||
if (!port::Zstd_GetUncompressedLength(data, n, &ulength)) {
|
||||
delete[] buf;
|
||||
return Status::Corruption("corrupted zstd compressed block length");
|
||||
}
|
||||
char* ubuf = new char[ulength];
|
||||
if (!port::Zstd_Uncompress(data, n, ubuf)) {
|
||||
delete[] buf;
|
||||
delete[] ubuf;
|
||||
return Status::Corruption("corrupted zstd compressed block contents");
|
||||
}
|
||||
delete[] buf;
|
||||
result->data = Slice(ubuf, ulength);
|
||||
result->heap_allocated = true;
|
||||
result->cachable = true;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
delete[] buf;
|
||||
return Status::Corruption("bad block type");
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
55
3rdparty/leveldb/src/hash.cc
vendored
Normal file
55
3rdparty/leveldb/src/hash.cc
vendored
Normal file
@@ -0,0 +1,55 @@
|
||||
// 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/hash.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "util/coding.h"
|
||||
|
||||
// The FALLTHROUGH_INTENDED macro can be used to annotate implicit fall-through
|
||||
// between switch labels. The real definition should be provided externally.
|
||||
// This one is a fallback version for unsupported compilers.
|
||||
#ifndef FALLTHROUGH_INTENDED
|
||||
#define FALLTHROUGH_INTENDED \
|
||||
do { \
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
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 += static_cast<uint8_t>(data[2]) << 16;
|
||||
FALLTHROUGH_INTENDED;
|
||||
case 2:
|
||||
h += static_cast<uint8_t>(data[1]) << 8;
|
||||
FALLTHROUGH_INTENDED;
|
||||
case 1:
|
||||
h += static_cast<uint8_t>(data[0]);
|
||||
h *= m;
|
||||
h ^= (h >> r);
|
||||
break;
|
||||
}
|
||||
return h;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
76
3rdparty/leveldb/src/iterator.cc
vendored
Normal file
76
3rdparty/leveldb/src/iterator.cc
vendored
Normal file
@@ -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 "leveldb/iterator.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
Iterator::Iterator() {
|
||||
cleanup_head_.function = nullptr;
|
||||
cleanup_head_.next = nullptr;
|
||||
}
|
||||
|
||||
Iterator::~Iterator() {
|
||||
if (!cleanup_head_.IsEmpty()) {
|
||||
cleanup_head_.Run();
|
||||
for (CleanupNode* node = cleanup_head_.next; node != nullptr;) {
|
||||
node->Run();
|
||||
CleanupNode* next_node = node->next;
|
||||
delete node;
|
||||
node = next_node;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Iterator::RegisterCleanup(CleanupFunction func, void* arg1, void* arg2) {
|
||||
assert(func != nullptr);
|
||||
CleanupNode* node;
|
||||
if (cleanup_head_.IsEmpty()) {
|
||||
node = &cleanup_head_;
|
||||
} else {
|
||||
node = new CleanupNode();
|
||||
node->next = cleanup_head_.next;
|
||||
cleanup_head_.next = node;
|
||||
}
|
||||
node->function = func;
|
||||
node->arg1 = arg1;
|
||||
node->arg2 = arg2;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
class EmptyIterator : public Iterator {
|
||||
public:
|
||||
EmptyIterator(const Status& s) : status_(s) {}
|
||||
~EmptyIterator() override = default;
|
||||
|
||||
bool Valid() const override { return false; }
|
||||
void Seek(const Slice& target) override {}
|
||||
void SeekToFirst() override {}
|
||||
void SeekToLast() override {}
|
||||
void Next() override { assert(false); }
|
||||
void Prev() override { assert(false); }
|
||||
Slice key() const override {
|
||||
assert(false);
|
||||
return Slice();
|
||||
}
|
||||
Slice value() const override {
|
||||
assert(false);
|
||||
return Slice();
|
||||
}
|
||||
Status status() const override { return status_; }
|
||||
|
||||
private:
|
||||
Status status_;
|
||||
};
|
||||
|
||||
} // anonymous namespace
|
||||
|
||||
Iterator* NewEmptyIterator() { return new EmptyIterator(Status::OK()); }
|
||||
|
||||
Iterator* NewErrorIterator(const Status& status) {
|
||||
return new EmptyIterator(status);
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
274
3rdparty/leveldb/src/log_reader.cc
vendored
Normal file
274
3rdparty/leveldb/src/log_reader.cc
vendored
Normal file
@@ -0,0 +1,274 @@
|
||||
// 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 <cstdio>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
|
||||
namespace leveldb {
|
||||
namespace log {
|
||||
|
||||
Reader::Reporter::~Reporter() = default;
|
||||
|
||||
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),
|
||||
resyncing_(initial_offset > 0) {}
|
||||
|
||||
Reader::~Reader() { delete[] backing_store_; }
|
||||
|
||||
bool Reader::SkipToInitialBlock() {
|
||||
const 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) {
|
||||
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) {
|
||||
const unsigned int record_type = ReadPhysicalRecord(&fragment);
|
||||
|
||||
// ReadPhysicalRecord may have only had an empty trailer remaining in its
|
||||
// internal buffer. Calculate the offset of the next physical record now
|
||||
// that it has returned, properly accounting for its header size.
|
||||
uint64_t physical_record_offset =
|
||||
end_of_buffer_offset_ - buffer_.size() - kHeaderSize - fragment.size();
|
||||
|
||||
if (resyncing_) {
|
||||
if (record_type == kMiddleType) {
|
||||
continue;
|
||||
} else if (record_type == kLastType) {
|
||||
resyncing_ = false;
|
||||
continue;
|
||||
} else {
|
||||
resyncing_ = false;
|
||||
}
|
||||
}
|
||||
|
||||
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()) {
|
||||
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()) {
|
||||
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) {
|
||||
// This can be caused by the writer dying immediately after
|
||||
// writing a physical record but before completing the next; don't
|
||||
// treat it as a corruption, just ignore the entire logical record.
|
||||
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];
|
||||
std::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(uint64_t bytes, const char* reason) {
|
||||
ReportDrop(bytes, Status::Corruption(reason));
|
||||
}
|
||||
|
||||
void Reader::ReportDrop(uint64_t bytes, const Status& reason) {
|
||||
if (reporter_ != nullptr &&
|
||||
end_of_buffer_offset_ - buffer_.size() - bytes >= initial_offset_) {
|
||||
reporter_->Corruption(static_cast<size_t>(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 {
|
||||
// Note that if buffer_ is non-empty, we have a truncated header at the
|
||||
// end of the file, which can be caused by the writer crashing in the
|
||||
// middle of writing the header. Instead of considering this an error,
|
||||
// just report EOF.
|
||||
buffer_.clear();
|
||||
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();
|
||||
if (!eof_) {
|
||||
ReportCorruption(drop_size, "bad record length");
|
||||
return kBadRecord;
|
||||
}
|
||||
// If the end of the file has been reached without reading |length| bytes
|
||||
// of payload, assume the writer died in the middle of writing the record.
|
||||
// Don't report a corruption.
|
||||
return kEof;
|
||||
}
|
||||
|
||||
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
|
||||
111
3rdparty/leveldb/src/log_writer.cc
vendored
Normal file
111
3rdparty/leveldb/src/log_writer.cc
vendored
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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 <cstdint>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
|
||||
namespace leveldb {
|
||||
namespace log {
|
||||
|
||||
static void InitTypeCrc(uint32_t* type_crc) {
|
||||
for (int i = 0; i <= kMaxRecordType; i++) {
|
||||
char t = static_cast<char>(i);
|
||||
type_crc[i] = crc32c::Value(&t, 1);
|
||||
}
|
||||
}
|
||||
|
||||
Writer::Writer(WritableFile* dest) : dest_(dest), block_offset_(0) {
|
||||
InitTypeCrc(type_crc_);
|
||||
}
|
||||
|
||||
Writer::Writer(WritableFile* dest, uint64_t dest_length)
|
||||
: dest_(dest), block_offset_(dest_length % kBlockSize) {
|
||||
InitTypeCrc(type_crc_);
|
||||
}
|
||||
|
||||
Writer::~Writer() = default;
|
||||
|
||||
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)
|
||||
static_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 length) {
|
||||
assert(length <= 0xffff); // Must fit in two bytes
|
||||
assert(block_offset_ + kHeaderSize + length <= kBlockSize);
|
||||
|
||||
// Format the header
|
||||
char buf[kHeaderSize];
|
||||
buf[4] = static_cast<char>(length & 0xff);
|
||||
buf[5] = static_cast<char>(length >> 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, length);
|
||||
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, length));
|
||||
if (s.ok()) {
|
||||
s = dest_->Flush();
|
||||
}
|
||||
}
|
||||
block_offset_ += kHeaderSize + length;
|
||||
return s;
|
||||
}
|
||||
|
||||
} // namespace log
|
||||
} // namespace leveldb
|
||||
82
3rdparty/leveldb/src/logging.cc
vendored
Normal file
82
3rdparty/leveldb/src/logging.cc
vendored
Normal file
@@ -0,0 +1,82 @@
|
||||
// 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 <cstdarg>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/slice.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
void AppendNumberTo(std::string* str, uint64_t num) {
|
||||
char buf[30];
|
||||
std::snprintf(buf, sizeof(buf), "%llu", static_cast<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];
|
||||
std::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 ConsumeDecimalNumber(Slice* in, uint64_t* val) {
|
||||
// Constants that will be optimized away.
|
||||
constexpr const uint64_t kMaxUint64 = std::numeric_limits<uint64_t>::max();
|
||||
constexpr const char kLastDigitOfMaxUint64 =
|
||||
'0' + static_cast<char>(kMaxUint64 % 10);
|
||||
|
||||
uint64_t value = 0;
|
||||
|
||||
// reinterpret_cast-ing from char* to uint8_t* to avoid signedness.
|
||||
const uint8_t* start = reinterpret_cast<const uint8_t*>(in->data());
|
||||
|
||||
const uint8_t* end = start + in->size();
|
||||
const uint8_t* current = start;
|
||||
for (; current != end; ++current) {
|
||||
const uint8_t ch = *current;
|
||||
if (ch < '0' || ch > '9') break;
|
||||
|
||||
// Overflow check.
|
||||
// kMaxUint64 / 10 is also constant and will be optimized away.
|
||||
if (value > kMaxUint64 / 10 ||
|
||||
(value == kMaxUint64 / 10 && ch > kLastDigitOfMaxUint64)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
value = (value * 10) + (ch - '0');
|
||||
}
|
||||
|
||||
*val = value;
|
||||
const size_t digits_consumed = current - start;
|
||||
in->remove_prefix(digits_consumed);
|
||||
return digits_consumed != 0;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
390
3rdparty/leveldb/src/memenv.cc
vendored
Normal file
390
3rdparty/leveldb/src/memenv.cc
vendored
Normal file
@@ -0,0 +1,390 @@
|
||||
// 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 <cstring>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/status.h"
|
||||
#include "port/port.h"
|
||||
#include "port/thread_annotations.h"
|
||||
#include "util/mutexlock.h"
|
||||
|
||||
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) {}
|
||||
|
||||
// No copying allowed.
|
||||
FileState(const FileState&) = delete;
|
||||
FileState& operator=(const FileState&) = delete;
|
||||
|
||||
// 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 {
|
||||
MutexLock lock(&blocks_mutex_);
|
||||
return size_;
|
||||
}
|
||||
|
||||
void Truncate() {
|
||||
MutexLock lock(&blocks_mutex_);
|
||||
for (char*& block : blocks_) {
|
||||
delete[] block;
|
||||
}
|
||||
blocks_.clear();
|
||||
size_ = 0;
|
||||
}
|
||||
|
||||
Status Read(uint64_t offset, size_t n, Slice* result, char* scratch) const {
|
||||
MutexLock lock(&blocks_mutex_);
|
||||
if (offset > size_) {
|
||||
return Status::IOError("Offset greater than file size.");
|
||||
}
|
||||
const uint64_t available = size_ - offset;
|
||||
if (n > available) {
|
||||
n = static_cast<size_t>(available);
|
||||
}
|
||||
if (n == 0) {
|
||||
*result = Slice();
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
assert(offset / kBlockSize <= std::numeric_limits<size_t>::max());
|
||||
size_t block = static_cast<size_t>(offset / kBlockSize);
|
||||
size_t block_offset = offset % kBlockSize;
|
||||
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;
|
||||
}
|
||||
std::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();
|
||||
|
||||
MutexLock lock(&blocks_mutex_);
|
||||
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;
|
||||
}
|
||||
std::memcpy(blocks_.back() + offset, src, avail);
|
||||
src_len -= avail;
|
||||
src += avail;
|
||||
size_ += avail;
|
||||
}
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
enum { kBlockSize = 8 * 1024 };
|
||||
|
||||
// Private since only Unref() should be used to delete it.
|
||||
~FileState() { Truncate(); }
|
||||
|
||||
port::Mutex refs_mutex_;
|
||||
int refs_ GUARDED_BY(refs_mutex_);
|
||||
|
||||
mutable port::Mutex blocks_mutex_;
|
||||
std::vector<char*> blocks_ GUARDED_BY(blocks_mutex_);
|
||||
uint64_t size_ GUARDED_BY(blocks_mutex_);
|
||||
};
|
||||
|
||||
class SequentialFileImpl : public SequentialFile {
|
||||
public:
|
||||
explicit SequentialFileImpl(FileState* file) : file_(file), pos_(0) {
|
||||
file_->Ref();
|
||||
}
|
||||
|
||||
~SequentialFileImpl() override { file_->Unref(); }
|
||||
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
Status s = file_->Read(pos_, n, result, scratch);
|
||||
if (s.ok()) {
|
||||
pos_ += result->size();
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Status Skip(uint64_t n) override {
|
||||
if (pos_ > file_->Size()) {
|
||||
return Status::IOError("pos_ > file_->Size()");
|
||||
}
|
||||
const uint64_t available = file_->Size() - pos_;
|
||||
if (n > available) {
|
||||
n = available;
|
||||
}
|
||||
pos_ += n;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
FileState* file_;
|
||||
uint64_t pos_;
|
||||
};
|
||||
|
||||
class RandomAccessFileImpl : public RandomAccessFile {
|
||||
public:
|
||||
explicit RandomAccessFileImpl(FileState* file) : file_(file) { file_->Ref(); }
|
||||
|
||||
~RandomAccessFileImpl() override { file_->Unref(); }
|
||||
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
return file_->Read(offset, n, result, scratch);
|
||||
}
|
||||
|
||||
private:
|
||||
FileState* file_;
|
||||
};
|
||||
|
||||
class WritableFileImpl : public WritableFile {
|
||||
public:
|
||||
WritableFileImpl(FileState* file) : file_(file) { file_->Ref(); }
|
||||
|
||||
~WritableFileImpl() override { file_->Unref(); }
|
||||
|
||||
Status Append(const Slice& data) override { return file_->Append(data); }
|
||||
|
||||
Status Close() override { return Status::OK(); }
|
||||
Status Flush() override { return Status::OK(); }
|
||||
Status Sync() override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
FileState* file_;
|
||||
};
|
||||
|
||||
class NoOpLogger : public Logger {
|
||||
public:
|
||||
void Logv(const char* format, std::va_list ap) override {}
|
||||
};
|
||||
|
||||
class InMemoryEnv : public EnvWrapper {
|
||||
public:
|
||||
explicit InMemoryEnv(Env* base_env) : EnvWrapper(base_env) {}
|
||||
|
||||
~InMemoryEnv() override {
|
||||
for (const auto& kvp : file_map_) {
|
||||
kvp.second->Unref();
|
||||
}
|
||||
}
|
||||
|
||||
// Partial implementation of the Env interface.
|
||||
Status NewSequentialFile(const std::string& fname,
|
||||
SequentialFile** result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(fname) == file_map_.end()) {
|
||||
*result = nullptr;
|
||||
return Status::IOError(fname, "File not found");
|
||||
}
|
||||
|
||||
*result = new SequentialFileImpl(file_map_[fname]);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewRandomAccessFile(const std::string& fname,
|
||||
RandomAccessFile** result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(fname) == file_map_.end()) {
|
||||
*result = nullptr;
|
||||
return Status::IOError(fname, "File not found");
|
||||
}
|
||||
|
||||
*result = new RandomAccessFileImpl(file_map_[fname]);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewWritableFile(const std::string& fname,
|
||||
WritableFile** result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
FileSystem::iterator it = file_map_.find(fname);
|
||||
|
||||
FileState* file;
|
||||
if (it == file_map_.end()) {
|
||||
// File is not currently open.
|
||||
file = new FileState();
|
||||
file->Ref();
|
||||
file_map_[fname] = file;
|
||||
} else {
|
||||
file = it->second;
|
||||
file->Truncate();
|
||||
}
|
||||
|
||||
*result = new WritableFileImpl(file);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewAppendableFile(const std::string& fname,
|
||||
WritableFile** result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
FileState** sptr = &file_map_[fname];
|
||||
FileState* file = *sptr;
|
||||
if (file == nullptr) {
|
||||
file = new FileState();
|
||||
file->Ref();
|
||||
}
|
||||
*result = new WritableFileImpl(file);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool FileExists(const std::string& fname) override {
|
||||
MutexLock lock(&mutex_);
|
||||
return file_map_.find(fname) != file_map_.end();
|
||||
}
|
||||
|
||||
Status GetChildren(const std::string& dir,
|
||||
std::vector<std::string>* result) override {
|
||||
MutexLock lock(&mutex_);
|
||||
result->clear();
|
||||
|
||||
for (const auto& kvp : file_map_) {
|
||||
const std::string& filename = kvp.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 RemoveFileInternal(const std::string& fname)
|
||||
EXCLUSIVE_LOCKS_REQUIRED(mutex_) {
|
||||
if (file_map_.find(fname) == file_map_.end()) {
|
||||
return;
|
||||
}
|
||||
|
||||
file_map_[fname]->Unref();
|
||||
file_map_.erase(fname);
|
||||
}
|
||||
|
||||
Status RemoveFile(const std::string& fname) override {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(fname) == file_map_.end()) {
|
||||
return Status::IOError(fname, "File not found");
|
||||
}
|
||||
|
||||
RemoveFileInternal(fname);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CreateDir(const std::string& dirname) override { return Status::OK(); }
|
||||
|
||||
Status RemoveDir(const std::string& dirname) override { return Status::OK(); }
|
||||
|
||||
Status GetFileSize(const std::string& fname, uint64_t* file_size) override {
|
||||
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();
|
||||
}
|
||||
|
||||
Status RenameFile(const std::string& src,
|
||||
const std::string& target) override {
|
||||
MutexLock lock(&mutex_);
|
||||
if (file_map_.find(src) == file_map_.end()) {
|
||||
return Status::IOError(src, "File not found");
|
||||
}
|
||||
|
||||
RemoveFileInternal(target);
|
||||
file_map_[target] = file_map_[src];
|
||||
file_map_.erase(src);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status LockFile(const std::string& fname, FileLock** lock) override {
|
||||
*lock = new FileLock;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status UnlockFile(FileLock* lock) override {
|
||||
delete lock;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetTestDirectory(std::string* path) override {
|
||||
*path = "/test";
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewLogger(const std::string& fname, Logger** result) override {
|
||||
*result = new NoOpLogger;
|
||||
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_ GUARDED_BY(mutex_);
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
Env* NewMemEnv(Env* base_env) { return new InMemoryEnv(base_env); }
|
||||
|
||||
} // namespace leveldb
|
||||
138
3rdparty/leveldb/src/memtable.cc
vendored
Normal file
138
3rdparty/leveldb/src/memtable.cc
vendored
Normal file
@@ -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.
|
||||
|
||||
#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& comparator)
|
||||
: comparator_(comparator), 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) {}
|
||||
|
||||
MemTableIterator(const MemTableIterator&) = delete;
|
||||
MemTableIterator& operator=(const MemTableIterator&) = delete;
|
||||
|
||||
~MemTableIterator() override = default;
|
||||
|
||||
bool Valid() const override { return iter_.Valid(); }
|
||||
void Seek(const Slice& k) override { iter_.Seek(EncodeKey(&tmp_, k)); }
|
||||
void SeekToFirst() override { iter_.SeekToFirst(); }
|
||||
void SeekToLast() override { iter_.SeekToLast(); }
|
||||
void Next() override { iter_.Next(); }
|
||||
void Prev() override { iter_.Prev(); }
|
||||
Slice key() const override { return GetLengthPrefixedSlice(iter_.key()); }
|
||||
Slice value() const override {
|
||||
Slice key_slice = GetLengthPrefixedSlice(iter_.key());
|
||||
return GetLengthPrefixedSlice(key_slice.data() + key_slice.size());
|
||||
}
|
||||
|
||||
Status status() const override { return Status::OK(); }
|
||||
|
||||
private:
|
||||
MemTable::Table::Iterator iter_;
|
||||
std::string tmp_; // For passing to EncodeKey
|
||||
};
|
||||
|
||||
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()]
|
||||
// tag : uint64((sequence << 8) | type)
|
||||
// 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);
|
||||
std::memcpy(p, key.data(), key_size);
|
||||
p += key_size;
|
||||
EncodeFixed64(p, (s << 8) | type);
|
||||
p += 8;
|
||||
p = EncodeVarint32(p, val_size);
|
||||
std::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
|
||||
191
3rdparty/leveldb/src/merger.cc
vendored
Normal file
191
3rdparty/leveldb/src/merger.cc
vendored
Normal file
@@ -0,0 +1,191 @@
|
||||
// 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_(nullptr),
|
||||
direction_(kForward) {
|
||||
for (int i = 0; i < n; i++) {
|
||||
children_[i].Set(children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
~MergingIterator() override { delete[] children_; }
|
||||
|
||||
bool Valid() const override { return (current_ != nullptr); }
|
||||
|
||||
void SeekToFirst() override {
|
||||
for (int i = 0; i < n_; i++) {
|
||||
children_[i].SeekToFirst();
|
||||
}
|
||||
FindSmallest();
|
||||
direction_ = kForward;
|
||||
}
|
||||
|
||||
void SeekToLast() override {
|
||||
for (int i = 0; i < n_; i++) {
|
||||
children_[i].SeekToLast();
|
||||
}
|
||||
FindLargest();
|
||||
direction_ = kReverse;
|
||||
}
|
||||
|
||||
void Seek(const Slice& target) override {
|
||||
for (int i = 0; i < n_; i++) {
|
||||
children_[i].Seek(target);
|
||||
}
|
||||
FindSmallest();
|
||||
direction_ = kForward;
|
||||
}
|
||||
|
||||
void Next() override {
|
||||
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();
|
||||
}
|
||||
|
||||
void Prev() override {
|
||||
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();
|
||||
}
|
||||
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return current_->key();
|
||||
}
|
||||
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
return current_->value();
|
||||
}
|
||||
|
||||
Status status() const override {
|
||||
Status status;
|
||||
for (int i = 0; i < n_; i++) {
|
||||
status = children_[i].status();
|
||||
if (!status.ok()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
// Which direction is the iterator moving?
|
||||
enum Direction { kForward, kReverse };
|
||||
|
||||
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_;
|
||||
Direction direction_;
|
||||
};
|
||||
|
||||
void MergingIterator::FindSmallest() {
|
||||
IteratorWrapper* smallest = nullptr;
|
||||
for (int i = 0; i < n_; i++) {
|
||||
IteratorWrapper* child = &children_[i];
|
||||
if (child->Valid()) {
|
||||
if (smallest == nullptr) {
|
||||
smallest = child;
|
||||
} else if (comparator_->Compare(child->key(), smallest->key()) < 0) {
|
||||
smallest = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
current_ = smallest;
|
||||
}
|
||||
|
||||
void MergingIterator::FindLargest() {
|
||||
IteratorWrapper* largest = nullptr;
|
||||
for (int i = n_ - 1; i >= 0; i--) {
|
||||
IteratorWrapper* child = &children_[i];
|
||||
if (child->Valid()) {
|
||||
if (largest == nullptr) {
|
||||
largest = child;
|
||||
} else if (comparator_->Compare(child->key(), largest->key()) > 0) {
|
||||
largest = child;
|
||||
}
|
||||
}
|
||||
}
|
||||
current_ = largest;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
Iterator* NewMergingIterator(const Comparator* comparator, Iterator** children,
|
||||
int n) {
|
||||
assert(n >= 0);
|
||||
if (n == 0) {
|
||||
return NewEmptyIterator();
|
||||
} else if (n == 1) {
|
||||
return children[0];
|
||||
} else {
|
||||
return new MergingIterator(comparator, children, n);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
14
3rdparty/leveldb/src/options.cc
vendored
Normal file
14
3rdparty/leveldb/src/options.cc
vendored
Normal file
@@ -0,0 +1,14 @@
|
||||
// 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()), env(Env::Default()) {}
|
||||
|
||||
} // namespace leveldb
|
||||
451
3rdparty/leveldb/src/repair.cc
vendored
Normal file
451
3rdparty/leveldb/src/repair.cc
vendored
Normal file
@@ -0,0 +1,451 @@
|
||||
// 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),
|
||||
ipolicy_(options.filter_policy),
|
||||
options_(SanitizeOptions(dbname, &icmp_, &ipolicy_, 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;
|
||||
};
|
||||
|
||||
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;
|
||||
void Corruption(size_t bytes, const Status& s) override {
|
||||
// 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 intentionally 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 = nullptr;
|
||||
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() {
|
||||
for (size_t i = 0; i < table_numbers_.size(); i++) {
|
||||
ScanTable(table_numbers_[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Iterator* NewTableIterator(const FileMetaData& meta) {
|
||||
// Same as compaction iterators: if paranoid_checks are on, turn
|
||||
// on checksum verification.
|
||||
ReadOptions r;
|
||||
r.verify_checksums = options_.paranoid_checks;
|
||||
return table_cache_->NewIterator(r, meta.number, meta.file_size);
|
||||
}
|
||||
|
||||
void ScanTable(uint64_t number) {
|
||||
TableInfo t;
|
||||
t.meta.number = number;
|
||||
std::string fname = TableFileName(dbname_, number);
|
||||
Status status = env_->GetFileSize(fname, &t.meta.file_size);
|
||||
if (!status.ok()) {
|
||||
// Try alternate file name.
|
||||
fname = SSTTableFileName(dbname_, number);
|
||||
Status s2 = env_->GetFileSize(fname, &t.meta.file_size);
|
||||
if (s2.ok()) {
|
||||
status = Status::OK();
|
||||
}
|
||||
}
|
||||
if (!status.ok()) {
|
||||
ArchiveFile(TableFileName(dbname_, number));
|
||||
ArchiveFile(SSTTableFileName(dbname_, number));
|
||||
Log(options_.info_log, "Table #%llu: dropped: %s",
|
||||
(unsigned long long)t.meta.number, status.ToString().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract metadata by scanning through table.
|
||||
int counter = 0;
|
||||
Iterator* iter = NewTableIterator(t.meta);
|
||||
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());
|
||||
|
||||
if (status.ok()) {
|
||||
tables_.push_back(t);
|
||||
} else {
|
||||
RepairTable(fname, t); // RepairTable archives input file.
|
||||
}
|
||||
}
|
||||
|
||||
void RepairTable(const std::string& src, TableInfo t) {
|
||||
// We will copy src contents to a new table and then rename the
|
||||
// new table over the source.
|
||||
|
||||
// Create builder.
|
||||
std::string copy = TableFileName(dbname_, next_file_number_++);
|
||||
WritableFile* file;
|
||||
Status s = env_->NewWritableFile(copy, &file);
|
||||
if (!s.ok()) {
|
||||
return;
|
||||
}
|
||||
TableBuilder* builder = new TableBuilder(options_, file);
|
||||
|
||||
// Copy data.
|
||||
Iterator* iter = NewTableIterator(t.meta);
|
||||
int counter = 0;
|
||||
for (iter->SeekToFirst(); iter->Valid(); iter->Next()) {
|
||||
builder->Add(iter->key(), iter->value());
|
||||
counter++;
|
||||
}
|
||||
delete iter;
|
||||
|
||||
ArchiveFile(src);
|
||||
if (counter == 0) {
|
||||
builder->Abandon(); // Nothing to save
|
||||
} else {
|
||||
s = builder->Finish();
|
||||
if (s.ok()) {
|
||||
t.meta.file_size = builder->FileSize();
|
||||
}
|
||||
}
|
||||
delete builder;
|
||||
builder = nullptr;
|
||||
|
||||
if (s.ok()) {
|
||||
s = file->Close();
|
||||
}
|
||||
delete file;
|
||||
file = nullptr;
|
||||
|
||||
if (counter > 0 && s.ok()) {
|
||||
std::string orig = TableFileName(dbname_, t.meta.number);
|
||||
s = env_->RenameFile(copy, orig);
|
||||
if (s.ok()) {
|
||||
Log(options_.info_log, "Table #%llu: %d entries repaired",
|
||||
(unsigned long long)t.meta.number, counter);
|
||||
tables_.push_back(t);
|
||||
}
|
||||
}
|
||||
if (!s.ok()) {
|
||||
env_->RemoveFile(copy);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// std::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 = nullptr;
|
||||
|
||||
if (!status.ok()) {
|
||||
env_->RemoveFile(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_->RemoveFile(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 != nullptr) {
|
||||
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 == nullptr) ? 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());
|
||||
}
|
||||
|
||||
const std::string dbname_;
|
||||
Env* const env_;
|
||||
InternalKeyComparator const icmp_;
|
||||
InternalFilterPolicy const ipolicy_;
|
||||
const Options 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_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
Status RepairDB(const std::string& dbname, const Options& options) {
|
||||
Repairer repairer(dbname, options);
|
||||
return repairer.Run();
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
77
3rdparty/leveldb/src/status.cc
vendored
Normal file
77
3rdparty/leveldb/src/status.cc
vendored
Normal file
@@ -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 "leveldb/status.h"
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
#include "port/port.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
const char* Status::CopyState(const char* state) {
|
||||
uint32_t size;
|
||||
std::memcpy(&size, state, sizeof(size));
|
||||
char* result = new char[size + 5];
|
||||
std::memcpy(result, state, size + 5);
|
||||
return result;
|
||||
}
|
||||
|
||||
Status::Status(Code code, const Slice& msg, const Slice& msg2) {
|
||||
assert(code != kOk);
|
||||
const uint32_t len1 = static_cast<uint32_t>(msg.size());
|
||||
const uint32_t len2 = static_cast<uint32_t>(msg2.size());
|
||||
const uint32_t size = len1 + (len2 ? (2 + len2) : 0);
|
||||
char* result = new char[size + 5];
|
||||
std::memcpy(result, &size, sizeof(size));
|
||||
result[4] = static_cast<char>(code);
|
||||
std::memcpy(result + 5, msg.data(), len1);
|
||||
if (len2) {
|
||||
result[5 + len1] = ':';
|
||||
result[6 + len1] = ' ';
|
||||
std::memcpy(result + 7 + len1, msg2.data(), len2);
|
||||
}
|
||||
state_ = result;
|
||||
}
|
||||
|
||||
std::string Status::ToString() const {
|
||||
if (state_ == nullptr) {
|
||||
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:
|
||||
std::snprintf(tmp, sizeof(tmp),
|
||||
"Unknown code(%d): ", static_cast<int>(code()));
|
||||
type = tmp;
|
||||
break;
|
||||
}
|
||||
std::string result(type);
|
||||
uint32_t length;
|
||||
std::memcpy(&length, state_, sizeof(length));
|
||||
result.append(state_ + 5, length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
271
3rdparty/leveldb/src/table.cc
vendored
Normal file
271
3rdparty/leveldb/src/table.cc
vendored
Normal file
@@ -0,0 +1,271 @@
|
||||
// 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/comparator.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/filter_policy.h"
|
||||
#include "leveldb/options.h"
|
||||
#include "table/block.h"
|
||||
#include "table/filter_block.h"
|
||||
#include "table/format.h"
|
||||
#include "table/two_level_iterator.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
struct Table::Rep {
|
||||
~Rep() {
|
||||
delete filter;
|
||||
delete[] filter_data;
|
||||
delete index_block;
|
||||
}
|
||||
|
||||
Options options;
|
||||
Status status;
|
||||
RandomAccessFile* file;
|
||||
uint64_t cache_id;
|
||||
FilterBlockReader* filter;
|
||||
const char* filter_data;
|
||||
|
||||
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 = nullptr;
|
||||
if (size < Footer::kEncodedLength) {
|
||||
return Status::Corruption("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
|
||||
BlockContents index_block_contents;
|
||||
ReadOptions opt;
|
||||
if (options.paranoid_checks) {
|
||||
opt.verify_checksums = true;
|
||||
}
|
||||
s = ReadBlock(file, opt, footer.index_handle(), &index_block_contents);
|
||||
|
||||
if (s.ok()) {
|
||||
// We've successfully read the footer and the index block: we're
|
||||
// ready to serve requests.
|
||||
Block* index_block = new Block(index_block_contents);
|
||||
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);
|
||||
rep->filter_data = nullptr;
|
||||
rep->filter = nullptr;
|
||||
*table = new Table(rep);
|
||||
(*table)->ReadMeta(footer);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void Table::ReadMeta(const Footer& footer) {
|
||||
if (rep_->options.filter_policy == nullptr) {
|
||||
return; // Do not need any metadata
|
||||
}
|
||||
|
||||
// TODO(sanjay): Skip this if footer.metaindex_handle() size indicates
|
||||
// it is an empty block.
|
||||
ReadOptions opt;
|
||||
if (rep_->options.paranoid_checks) {
|
||||
opt.verify_checksums = true;
|
||||
}
|
||||
BlockContents contents;
|
||||
if (!ReadBlock(rep_->file, opt, footer.metaindex_handle(), &contents).ok()) {
|
||||
// Do not propagate errors since meta info is not needed for operation
|
||||
return;
|
||||
}
|
||||
Block* meta = new Block(contents);
|
||||
|
||||
Iterator* iter = meta->NewIterator(BytewiseComparator());
|
||||
std::string key = "filter.";
|
||||
key.append(rep_->options.filter_policy->Name());
|
||||
iter->Seek(key);
|
||||
if (iter->Valid() && iter->key() == Slice(key)) {
|
||||
ReadFilter(iter->value());
|
||||
}
|
||||
delete iter;
|
||||
delete meta;
|
||||
}
|
||||
|
||||
void Table::ReadFilter(const Slice& filter_handle_value) {
|
||||
Slice v = filter_handle_value;
|
||||
BlockHandle filter_handle;
|
||||
if (!filter_handle.DecodeFrom(&v).ok()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We might want to unify with ReadBlock() if we start
|
||||
// requiring checksum verification in Table::Open.
|
||||
ReadOptions opt;
|
||||
if (rep_->options.paranoid_checks) {
|
||||
opt.verify_checksums = true;
|
||||
}
|
||||
BlockContents block;
|
||||
if (!ReadBlock(rep_->file, opt, filter_handle, &block).ok()) {
|
||||
return;
|
||||
}
|
||||
if (block.heap_allocated) {
|
||||
rep_->filter_data = block.data.data(); // Will need to delete later
|
||||
}
|
||||
rep_->filter = new FilterBlockReader(rep_->options.filter_policy, block.data);
|
||||
}
|
||||
|
||||
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 = nullptr;
|
||||
Cache::Handle* cache_handle = nullptr;
|
||||
|
||||
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()) {
|
||||
BlockContents contents;
|
||||
if (block_cache != nullptr) {
|
||||
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 != nullptr) {
|
||||
block = reinterpret_cast<Block*>(block_cache->Value(cache_handle));
|
||||
} else {
|
||||
s = ReadBlock(table->rep_->file, options, handle, &contents);
|
||||
if (s.ok()) {
|
||||
block = new Block(contents);
|
||||
if (contents.cachable && options.fill_cache) {
|
||||
cache_handle = block_cache->Insert(key, block, block->size(),
|
||||
&DeleteCachedBlock);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
s = ReadBlock(table->rep_->file, options, handle, &contents);
|
||||
if (s.ok()) {
|
||||
block = new Block(contents);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Iterator* iter;
|
||||
if (block != nullptr) {
|
||||
iter = block->NewIterator(table->rep_->options.comparator);
|
||||
if (cache_handle == nullptr) {
|
||||
iter->RegisterCleanup(&DeleteBlock, block, nullptr);
|
||||
} 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);
|
||||
}
|
||||
|
||||
Status Table::InternalGet(const ReadOptions& options, const Slice& k, void* arg,
|
||||
void (*handle_result)(void*, const Slice&,
|
||||
const Slice&)) {
|
||||
Status s;
|
||||
Iterator* iiter = rep_->index_block->NewIterator(rep_->options.comparator);
|
||||
iiter->Seek(k);
|
||||
if (iiter->Valid()) {
|
||||
Slice handle_value = iiter->value();
|
||||
FilterBlockReader* filter = rep_->filter;
|
||||
BlockHandle handle;
|
||||
if (filter != nullptr && handle.DecodeFrom(&handle_value).ok() &&
|
||||
!filter->KeyMayMatch(handle.offset(), k)) {
|
||||
// Not found
|
||||
} else {
|
||||
Iterator* block_iter = BlockReader(this, options, iiter->value());
|
||||
block_iter->Seek(k);
|
||||
if (block_iter->Valid()) {
|
||||
(*handle_result)(arg, block_iter->key(), block_iter->value());
|
||||
}
|
||||
s = block_iter->status();
|
||||
delete block_iter;
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = iiter->status();
|
||||
}
|
||||
delete iiter;
|
||||
return s;
|
||||
}
|
||||
|
||||
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
|
||||
280
3rdparty/leveldb/src/table_builder.cc
vendored
Normal file
280
3rdparty/leveldb/src/table_builder.cc
vendored
Normal file
@@ -0,0 +1,280 @@
|
||||
// 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 <cassert>
|
||||
|
||||
#include "leveldb/comparator.h"
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/filter_policy.h"
|
||||
#include "leveldb/options.h"
|
||||
#include "table/block_builder.h"
|
||||
#include "table/filter_block.h"
|
||||
#include "table/format.h"
|
||||
#include "util/coding.h"
|
||||
#include "util/crc32c.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
struct TableBuilder::Rep {
|
||||
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),
|
||||
filter_block(opt.filter_policy == nullptr
|
||||
? nullptr
|
||||
: new FilterBlockBuilder(opt.filter_policy)),
|
||||
pending_index_entry(false) {
|
||||
index_block_options.block_restart_interval = 1;
|
||||
}
|
||||
|
||||
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.
|
||||
FilterBlockBuilder* filter_block;
|
||||
|
||||
// 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;
|
||||
};
|
||||
|
||||
TableBuilder::TableBuilder(const Options& options, WritableFile* file)
|
||||
: rep_(new Rep(options, file)) {
|
||||
if (rep_->filter_block != nullptr) {
|
||||
rep_->filter_block->StartBlock(0);
|
||||
}
|
||||
}
|
||||
|
||||
TableBuilder::~TableBuilder() {
|
||||
assert(rep_->closed); // Catch errors where caller forgot to call Finish()
|
||||
delete rep_->filter_block;
|
||||
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;
|
||||
}
|
||||
|
||||
if (r->filter_block != nullptr) {
|
||||
r->filter_block->AddKey(key);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
if (r->filter_block != nullptr) {
|
||||
r->filter_block->StartBlock(r->offset);
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
case kZstdCompression: {
|
||||
std::string* compressed = &r->compressed_output;
|
||||
if (port::Zstd_Compress(r->options.zstd_compression_level, raw.data(),
|
||||
raw.size(), compressed) &&
|
||||
compressed->size() < raw.size() - (raw.size() / 8u)) {
|
||||
block_contents = *compressed;
|
||||
} else {
|
||||
// Zstd not supported, or compressed less than 12.5%, so just
|
||||
// store uncompressed form
|
||||
block_contents = raw;
|
||||
type = kNoCompression;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
WriteRawBlock(block_contents, type, handle);
|
||||
r->compressed_output.clear();
|
||||
block->Reset();
|
||||
}
|
||||
|
||||
void TableBuilder::WriteRawBlock(const Slice& block_contents,
|
||||
CompressionType type, BlockHandle* handle) {
|
||||
Rep* r = rep_;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Status TableBuilder::status() const { return rep_->status; }
|
||||
|
||||
Status TableBuilder::Finish() {
|
||||
Rep* r = rep_;
|
||||
Flush();
|
||||
assert(!r->closed);
|
||||
r->closed = true;
|
||||
|
||||
BlockHandle filter_block_handle, metaindex_block_handle, index_block_handle;
|
||||
|
||||
// Write filter block
|
||||
if (ok() && r->filter_block != nullptr) {
|
||||
WriteRawBlock(r->filter_block->Finish(), kNoCompression,
|
||||
&filter_block_handle);
|
||||
}
|
||||
|
||||
// Write metaindex block
|
||||
if (ok()) {
|
||||
BlockBuilder meta_index_block(&r->options);
|
||||
if (r->filter_block != nullptr) {
|
||||
// Add mapping from "filter.Name" to location of filter data
|
||||
std::string key = "filter.";
|
||||
key.append(r->options.filter_policy->Name());
|
||||
std::string handle_encoding;
|
||||
filter_block_handle.EncodeTo(&handle_encoding);
|
||||
meta_index_block.Add(key, handle_encoding);
|
||||
}
|
||||
|
||||
// TODO(postrelease): Add stats and other meta blocks
|
||||
WriteBlock(&meta_index_block, &metaindex_block_handle);
|
||||
}
|
||||
|
||||
// Write index block
|
||||
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);
|
||||
}
|
||||
|
||||
// Write footer
|
||||
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
|
||||
120
3rdparty/leveldb/src/table_cache.cc
vendored
Normal file
120
3rdparty/leveldb/src/table_cache.cc
vendored
Normal file
@@ -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 "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_; }
|
||||
|
||||
Status TableCache::FindTable(uint64_t file_number, uint64_t file_size,
|
||||
Cache::Handle** handle) {
|
||||
Status s;
|
||||
char buf[sizeof(file_number)];
|
||||
EncodeFixed64(buf, file_number);
|
||||
Slice key(buf, sizeof(buf));
|
||||
*handle = cache_->Lookup(key);
|
||||
if (*handle == nullptr) {
|
||||
std::string fname = TableFileName(dbname_, file_number);
|
||||
RandomAccessFile* file = nullptr;
|
||||
Table* table = nullptr;
|
||||
s = env_->NewRandomAccessFile(fname, &file);
|
||||
if (!s.ok()) {
|
||||
std::string old_fname = SSTTableFileName(dbname_, file_number);
|
||||
if (env_->NewRandomAccessFile(old_fname, &file).ok()) {
|
||||
s = Status::OK();
|
||||
}
|
||||
}
|
||||
if (s.ok()) {
|
||||
s = Table::Open(options_, file, file_size, &table);
|
||||
}
|
||||
|
||||
if (!s.ok()) {
|
||||
assert(table == nullptr);
|
||||
delete file;
|
||||
// We do not cache error results so that if the error is transient,
|
||||
// or somebody repairs the file, we recover automatically.
|
||||
} else {
|
||||
TableAndFile* tf = new TableAndFile;
|
||||
tf->file = file;
|
||||
tf->table = table;
|
||||
*handle = cache_->Insert(key, tf, 1, &DeleteEntry);
|
||||
}
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
Iterator* TableCache::NewIterator(const ReadOptions& options,
|
||||
uint64_t file_number, uint64_t file_size,
|
||||
Table** tableptr) {
|
||||
if (tableptr != nullptr) {
|
||||
*tableptr = nullptr;
|
||||
}
|
||||
|
||||
Cache::Handle* handle = nullptr;
|
||||
Status s = FindTable(file_number, file_size, &handle);
|
||||
if (!s.ok()) {
|
||||
return NewErrorIterator(s);
|
||||
}
|
||||
|
||||
Table* table = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
|
||||
Iterator* result = table->NewIterator(options);
|
||||
result->RegisterCleanup(&UnrefEntry, cache_, handle);
|
||||
if (tableptr != nullptr) {
|
||||
*tableptr = table;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
Status TableCache::Get(const ReadOptions& options, uint64_t file_number,
|
||||
uint64_t file_size, const Slice& k, void* arg,
|
||||
void (*handle_result)(void*, const Slice&,
|
||||
const Slice&)) {
|
||||
Cache::Handle* handle = nullptr;
|
||||
Status s = FindTable(file_number, file_size, &handle);
|
||||
if (s.ok()) {
|
||||
Table* t = reinterpret_cast<TableAndFile*>(cache_->Value(handle))->table;
|
||||
s = t->InternalGet(options, k, arg, handle_result);
|
||||
cache_->Release(handle);
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
void TableCache::Evict(uint64_t file_number) {
|
||||
char buf[sizeof(file_number)];
|
||||
EncodeFixed64(buf, file_number);
|
||||
cache_->Erase(Slice(buf, sizeof(buf)));
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
171
3rdparty/leveldb/src/two_level_iterator.cc
vendored
Normal file
171
3rdparty/leveldb/src/two_level_iterator.cc
vendored
Normal file
@@ -0,0 +1,171 @@
|
||||
// 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);
|
||||
|
||||
~TwoLevelIterator() override;
|
||||
|
||||
void Seek(const Slice& target) override;
|
||||
void SeekToFirst() override;
|
||||
void SeekToLast() override;
|
||||
void Next() override;
|
||||
void Prev() override;
|
||||
|
||||
bool Valid() const override { return data_iter_.Valid(); }
|
||||
Slice key() const override {
|
||||
assert(Valid());
|
||||
return data_iter_.key();
|
||||
}
|
||||
Slice value() const override {
|
||||
assert(Valid());
|
||||
return data_iter_.value();
|
||||
}
|
||||
Status status() const override {
|
||||
// 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() != nullptr && !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 nullptr
|
||||
// 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_(nullptr) {}
|
||||
|
||||
TwoLevelIterator::~TwoLevelIterator() = default;
|
||||
|
||||
void TwoLevelIterator::Seek(const Slice& target) {
|
||||
index_iter_.Seek(target);
|
||||
InitDataBlock();
|
||||
if (data_iter_.iter() != nullptr) data_iter_.Seek(target);
|
||||
SkipEmptyDataBlocksForward();
|
||||
}
|
||||
|
||||
void TwoLevelIterator::SeekToFirst() {
|
||||
index_iter_.SeekToFirst();
|
||||
InitDataBlock();
|
||||
if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst();
|
||||
SkipEmptyDataBlocksForward();
|
||||
}
|
||||
|
||||
void TwoLevelIterator::SeekToLast() {
|
||||
index_iter_.SeekToLast();
|
||||
InitDataBlock();
|
||||
if (data_iter_.iter() != nullptr) 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() == nullptr || !data_iter_.Valid()) {
|
||||
// Move to next block
|
||||
if (!index_iter_.Valid()) {
|
||||
SetDataIterator(nullptr);
|
||||
return;
|
||||
}
|
||||
index_iter_.Next();
|
||||
InitDataBlock();
|
||||
if (data_iter_.iter() != nullptr) data_iter_.SeekToFirst();
|
||||
}
|
||||
}
|
||||
|
||||
void TwoLevelIterator::SkipEmptyDataBlocksBackward() {
|
||||
while (data_iter_.iter() == nullptr || !data_iter_.Valid()) {
|
||||
// Move to next block
|
||||
if (!index_iter_.Valid()) {
|
||||
SetDataIterator(nullptr);
|
||||
return;
|
||||
}
|
||||
index_iter_.Prev();
|
||||
InitDataBlock();
|
||||
if (data_iter_.iter() != nullptr) data_iter_.SeekToLast();
|
||||
}
|
||||
}
|
||||
|
||||
void TwoLevelIterator::SetDataIterator(Iterator* data_iter) {
|
||||
if (data_iter_.iter() != nullptr) SaveError(data_iter_.status());
|
||||
data_iter_.Set(data_iter);
|
||||
}
|
||||
|
||||
void TwoLevelIterator::InitDataBlock() {
|
||||
if (!index_iter_.Valid()) {
|
||||
SetDataIterator(nullptr);
|
||||
} else {
|
||||
Slice handle = index_iter_.value();
|
||||
if (data_iter_.iter() != nullptr &&
|
||||
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
|
||||
258
3rdparty/leveldb/src/version_edit.cc
vendored
Normal file
258
3rdparty/leveldb/src/version_edit.cc
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
// 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;
|
||||
compact_pointers_.clear();
|
||||
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 (const auto& deleted_file_kvp : deleted_files_) {
|
||||
PutVarint32(dst, kDeletedFile);
|
||||
PutVarint32(dst, deleted_file_kvp.first); // level
|
||||
PutVarint64(dst, deleted_file_kvp.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)) {
|
||||
return dst->DecodeFrom(str);
|
||||
} 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 = nullptr;
|
||||
uint32_t tag;
|
||||
|
||||
// Temporary storage for parsing
|
||||
int level;
|
||||
uint64_t number;
|
||||
FileMetaData f;
|
||||
Slice str;
|
||||
InternalKey key;
|
||||
|
||||
while (msg == nullptr && 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 == nullptr && !input.empty()) {
|
||||
msg = "invalid tag";
|
||||
}
|
||||
|
||||
Status result;
|
||||
if (msg != nullptr) {
|
||||
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 (const auto& deleted_files_kvp : deleted_files_) {
|
||||
r.append("\n RemoveFile: ");
|
||||
AppendNumberTo(&r, deleted_files_kvp.first);
|
||||
r.append(" ");
|
||||
AppendNumberTo(&r, deleted_files_kvp.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
|
||||
1569
3rdparty/leveldb/src/version_set.cc
vendored
Normal file
1569
3rdparty/leveldb/src/version_set.cc
vendored
Normal file
File diff suppressed because it is too large
Load Diff
150
3rdparty/leveldb/src/write_batch.cc
vendored
Normal file
150
3rdparty/leveldb/src/write_batch.cc
vendored
Normal file
@@ -0,0 +1,150 @@
|
||||
// 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 "db/dbformat.h"
|
||||
#include "db/memtable.h"
|
||||
#include "db/write_batch_internal.h"
|
||||
#include "leveldb/db.h"
|
||||
#include "util/coding.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
// WriteBatch header has an 8-byte sequence number followed by a 4-byte count.
|
||||
static const size_t kHeader = 12;
|
||||
|
||||
WriteBatch::WriteBatch() { Clear(); }
|
||||
|
||||
WriteBatch::~WriteBatch() = default;
|
||||
|
||||
WriteBatch::Handler::~Handler() = default;
|
||||
|
||||
void WriteBatch::Clear() {
|
||||
rep_.clear();
|
||||
rep_.resize(kHeader);
|
||||
}
|
||||
|
||||
size_t WriteBatch::ApproximateSize() const { return rep_.size(); }
|
||||
|
||||
Status WriteBatch::Iterate(Handler* handler) const {
|
||||
Slice input(rep_);
|
||||
if (input.size() < kHeader) {
|
||||
return Status::Corruption("malformed WriteBatch (too small)");
|
||||
}
|
||||
|
||||
input.remove_prefix(kHeader);
|
||||
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);
|
||||
}
|
||||
|
||||
void WriteBatch::Append(const WriteBatch& source) {
|
||||
WriteBatchInternal::Append(this, &source);
|
||||
}
|
||||
|
||||
namespace {
|
||||
class MemTableInserter : public WriteBatch::Handler {
|
||||
public:
|
||||
SequenceNumber sequence_;
|
||||
MemTable* mem_;
|
||||
|
||||
void Put(const Slice& key, const Slice& value) override {
|
||||
mem_->Add(sequence_, kTypeValue, key, value);
|
||||
sequence_++;
|
||||
}
|
||||
void Delete(const Slice& key) override {
|
||||
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() >= kHeader);
|
||||
b->rep_.assign(contents.data(), contents.size());
|
||||
}
|
||||
|
||||
void WriteBatchInternal::Append(WriteBatch* dst, const WriteBatch* src) {
|
||||
SetCount(dst, Count(dst) + Count(src));
|
||||
assert(src->rep_.size() >= kHeader);
|
||||
dst->rep_.append(src->rep_.data() + kHeader, src->rep_.size() - kHeader);
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
122
3rdparty/leveldb/status.h
vendored
Normal file
122
3rdparty/leveldb/status.h
vendored
Normal file
@@ -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.
|
||||
//
|
||||
// A Status encapsulates the result of an operation. It may indicate success,
|
||||
// or it may indicate an error with an associated error message.
|
||||
//
|
||||
// Multiple threads can invoke const methods on a Status without
|
||||
// external synchronization, but if any of the threads may call a
|
||||
// non-const method, all threads accessing the same Status must use
|
||||
// external synchronization.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_STATUS_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <string>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
#include "leveldb/slice.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class LEVELDB_EXPORT Status {
|
||||
public:
|
||||
// Create a success status.
|
||||
Status() noexcept : state_(nullptr) {}
|
||||
~Status() { delete[] state_; }
|
||||
|
||||
Status(const Status& rhs);
|
||||
Status& operator=(const Status& rhs);
|
||||
|
||||
Status(Status&& rhs) noexcept : state_(rhs.state_) { rhs.state_ = nullptr; }
|
||||
Status& operator=(Status&& rhs) noexcept;
|
||||
|
||||
// Return a success status.
|
||||
static Status OK() { return Status(); }
|
||||
|
||||
// Return error status of an appropriate type.
|
||||
static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) {
|
||||
return Status(kNotFound, msg, msg2);
|
||||
}
|
||||
static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) {
|
||||
return Status(kCorruption, msg, msg2);
|
||||
}
|
||||
static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) {
|
||||
return Status(kNotSupported, msg, msg2);
|
||||
}
|
||||
static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) {
|
||||
return Status(kInvalidArgument, msg, msg2);
|
||||
}
|
||||
static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) {
|
||||
return Status(kIOError, msg, msg2);
|
||||
}
|
||||
|
||||
// Returns true iff the status indicates success.
|
||||
bool ok() const { return (state_ == nullptr); }
|
||||
|
||||
// Returns true iff the status indicates a NotFound error.
|
||||
bool IsNotFound() const { return code() == kNotFound; }
|
||||
|
||||
// Returns true iff the status indicates a Corruption error.
|
||||
bool IsCorruption() const { return code() == kCorruption; }
|
||||
|
||||
// Returns true iff the status indicates an IOError.
|
||||
bool IsIOError() const { return code() == kIOError; }
|
||||
|
||||
// Returns true iff the status indicates a NotSupportedError.
|
||||
bool IsNotSupportedError() const { return code() == kNotSupported; }
|
||||
|
||||
// Returns true iff the status indicates an InvalidArgument.
|
||||
bool IsInvalidArgument() const { return code() == kInvalidArgument; }
|
||||
|
||||
// Return a string representation of this status suitable for printing.
|
||||
// Returns the string "OK" for success.
|
||||
std::string ToString() const;
|
||||
|
||||
private:
|
||||
enum Code {
|
||||
kOk = 0,
|
||||
kNotFound = 1,
|
||||
kCorruption = 2,
|
||||
kNotSupported = 3,
|
||||
kInvalidArgument = 4,
|
||||
kIOError = 5
|
||||
};
|
||||
|
||||
Code code() const {
|
||||
return (state_ == nullptr) ? kOk : static_cast<Code>(state_[4]);
|
||||
}
|
||||
|
||||
Status(Code code, const Slice& msg, const Slice& msg2);
|
||||
static const char* CopyState(const char* s);
|
||||
|
||||
// OK status has a null state_. Otherwise, state_ is a new[] array
|
||||
// of the following form:
|
||||
// state_[0..3] == length of message
|
||||
// state_[4] == code
|
||||
// state_[5..] == message
|
||||
const char* state_;
|
||||
};
|
||||
|
||||
inline Status::Status(const Status& rhs) {
|
||||
state_ = (rhs.state_ == nullptr) ? nullptr : CopyState(rhs.state_);
|
||||
}
|
||||
inline Status& Status::operator=(const Status& rhs) {
|
||||
// The following condition catches both aliasing (when this == &rhs),
|
||||
// and the common case where both rhs and *this are ok.
|
||||
if (state_ != rhs.state_) {
|
||||
delete[] state_;
|
||||
state_ = (rhs.state_ == nullptr) ? nullptr : CopyState(rhs.state_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
inline Status& Status::operator=(Status&& rhs) noexcept {
|
||||
std::swap(state_, rhs.state_);
|
||||
return *this;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_
|
||||
84
3rdparty/leveldb/table.h
vendored
Normal file
84
3rdparty/leveldb/table.h
vendored
Normal file
@@ -0,0 +1,84 @@
|
||||
// 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_INCLUDE_TABLE_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_TABLE_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
#include "leveldb/iterator.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class Block;
|
||||
class BlockHandle;
|
||||
class Footer;
|
||||
struct Options;
|
||||
class RandomAccessFile;
|
||||
struct ReadOptions;
|
||||
class TableCache;
|
||||
|
||||
// A Table is a sorted map from strings to strings. Tables are
|
||||
// immutable and persistent. A Table may be safely accessed from
|
||||
// multiple threads without external synchronization.
|
||||
class LEVELDB_EXPORT Table {
|
||||
public:
|
||||
// Attempt to open the table that is stored in bytes [0..file_size)
|
||||
// of "file", and read the metadata entries necessary to allow
|
||||
// retrieving data from the table.
|
||||
//
|
||||
// If successful, returns ok and sets "*table" to the newly opened
|
||||
// table. The client should delete "*table" when no longer needed.
|
||||
// If there was an error while initializing the table, sets "*table"
|
||||
// to nullptr and returns a non-ok status. Does not take ownership of
|
||||
// "*source", but the client must ensure that "source" remains live
|
||||
// for the duration of the returned table's lifetime.
|
||||
//
|
||||
// *file must remain live while this Table is in use.
|
||||
static Status Open(const Options& options, RandomAccessFile* file,
|
||||
uint64_t file_size, Table** table);
|
||||
|
||||
Table(const Table&) = delete;
|
||||
Table& operator=(const Table&) = delete;
|
||||
|
||||
~Table();
|
||||
|
||||
// Returns a new iterator over the table contents.
|
||||
// The result of NewIterator() is initially invalid (caller must
|
||||
// call one of the Seek methods on the iterator before using it).
|
||||
Iterator* NewIterator(const ReadOptions&) const;
|
||||
|
||||
// Given a key, return an approximate byte offset in the file where
|
||||
// the data for that key begins (or would begin if the key were
|
||||
// present in the file). The returned value is in terms of file
|
||||
// bytes, and so includes effects like compression of the underlying data.
|
||||
// E.g., the approximate offset of the last key in the table will
|
||||
// be close to the file length.
|
||||
uint64_t ApproximateOffsetOf(const Slice& key) const;
|
||||
|
||||
private:
|
||||
friend class TableCache;
|
||||
struct Rep;
|
||||
|
||||
static Iterator* BlockReader(void*, const ReadOptions&, const Slice&);
|
||||
|
||||
explicit Table(Rep* rep) : rep_(rep) {}
|
||||
|
||||
// Calls (*handle_result)(arg, ...) with the entry found after a call
|
||||
// to Seek(key). May not make such a call if filter policy says
|
||||
// that key is not present.
|
||||
Status InternalGet(const ReadOptions&, const Slice& key, void* arg,
|
||||
void (*handle_result)(void* arg, const Slice& k,
|
||||
const Slice& v));
|
||||
|
||||
void ReadMeta(const Footer& footer);
|
||||
void ReadFilter(const Slice& filter_handle_value);
|
||||
|
||||
Rep* const rep_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_TABLE_H_
|
||||
93
3rdparty/leveldb/table_builder.h
vendored
Normal file
93
3rdparty/leveldb/table_builder.h
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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.
|
||||
//
|
||||
// TableBuilder provides the interface used to build a Table
|
||||
// (an immutable and sorted map from keys to values).
|
||||
//
|
||||
// Multiple threads can invoke const methods on a TableBuilder without
|
||||
// external synchronization, but if any of the threads may call a
|
||||
// non-const method, all threads accessing the same TableBuilder must use
|
||||
// external synchronization.
|
||||
|
||||
#ifndef STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_
|
||||
#define STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "leveldb/export.h"
|
||||
#include "leveldb/options.h"
|
||||
#include "leveldb/status.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
class BlockBuilder;
|
||||
class BlockHandle;
|
||||
class WritableFile;
|
||||
|
||||
class LEVELDB_EXPORT TableBuilder {
|
||||
public:
|
||||
// Create a builder that will store the contents of the table it is
|
||||
// building in *file. Does not close the file. It is up to the
|
||||
// caller to close the file after calling Finish().
|
||||
TableBuilder(const Options& options, WritableFile* file);
|
||||
|
||||
TableBuilder(const TableBuilder&) = delete;
|
||||
TableBuilder& operator=(const TableBuilder&) = delete;
|
||||
|
||||
// REQUIRES: Either Finish() or Abandon() has been called.
|
||||
~TableBuilder();
|
||||
|
||||
// Change the options used by this builder. Note: only some of the
|
||||
// option fields can be changed after construction. If a field is
|
||||
// not allowed to change dynamically and its value in the structure
|
||||
// passed to the constructor is different from its value in the
|
||||
// structure passed to this method, this method will return an error
|
||||
// without changing any fields.
|
||||
Status ChangeOptions(const Options& options);
|
||||
|
||||
// Add key,value to the table being constructed.
|
||||
// REQUIRES: key is after any previously added key according to comparator.
|
||||
// REQUIRES: Finish(), Abandon() have not been called
|
||||
void Add(const Slice& key, const Slice& value);
|
||||
|
||||
// Advanced operation: flush any buffered key/value pairs to file.
|
||||
// Can be used to ensure that two adjacent entries never live in
|
||||
// the same data block. Most clients should not need to use this method.
|
||||
// REQUIRES: Finish(), Abandon() have not been called
|
||||
void Flush();
|
||||
|
||||
// Return non-ok iff some error has been detected.
|
||||
Status status() const;
|
||||
|
||||
// Finish building the table. Stops using the file passed to the
|
||||
// constructor after this function returns.
|
||||
// REQUIRES: Finish(), Abandon() have not been called
|
||||
Status Finish();
|
||||
|
||||
// Indicate that the contents of this builder should be abandoned. Stops
|
||||
// using the file passed to the constructor after this function returns.
|
||||
// If the caller is not going to call Finish(), it must call Abandon()
|
||||
// before destroying this builder.
|
||||
// REQUIRES: Finish(), Abandon() have not been called
|
||||
void Abandon();
|
||||
|
||||
// Number of calls to Add() so far.
|
||||
uint64_t NumEntries() const;
|
||||
|
||||
// Size of the file generated so far. If invoked after a successful
|
||||
// Finish() call, returns the size of the final generated file.
|
||||
uint64_t FileSize() const;
|
||||
|
||||
private:
|
||||
bool ok() const { return status().ok(); }
|
||||
void WriteBlock(BlockBuilder* block, BlockHandle* handle);
|
||||
void WriteRawBlock(const Slice& data, CompressionType, BlockHandle* handle);
|
||||
|
||||
struct Rep;
|
||||
Rep* rep_;
|
||||
};
|
||||
|
||||
} // namespace leveldb
|
||||
|
||||
#endif // STORAGE_LEVELDB_INCLUDE_TABLE_BUILDER_H_
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user