create
This commit is contained in:
66
Pods/leveldb-library/util/arena.cc
generated
Normal file
66
Pods/leveldb-library/util/arena.cc
generated
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
|
||||
71
Pods/leveldb-library/util/arena.h
generated
Normal file
71
Pods/leveldb-library/util/arena.h
generated
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_
|
||||
92
Pods/leveldb-library/util/bloom.cc
generated
Normal file
92
Pods/leveldb-library/util/bloom.cc
generated
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
|
||||
401
Pods/leveldb-library/util/cache.cc
generated
Normal file
401
Pods/leveldb-library/util/cache.cc
generated
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
|
||||
166
Pods/leveldb-library/util/coding.cc
generated
Normal file
166
Pods/leveldb-library/util/coding.cc
generated
Normal file
@@ -0,0 +1,166 @@
|
||||
// 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, (uint32_t)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;
|
||||
}
|
||||
}
|
||||
|
||||
const char* GetLengthPrefixedSlice(const char* p, const char* limit,
|
||||
Slice* result) {
|
||||
uint32_t len;
|
||||
p = GetVarint32Ptr(p, limit, &len);
|
||||
if (p == nullptr) return nullptr;
|
||||
if (p + len > limit) return nullptr;
|
||||
*result = Slice(p, len);
|
||||
return p + len;
|
||||
}
|
||||
|
||||
bool GetLengthPrefixedSlice(Slice* input, Slice* result) {
|
||||
uint32_t len;
|
||||
if (GetVarint32(input, &len) && input->size() >= len) {
|
||||
*result = Slice(input->data(), len);
|
||||
input->remove_prefix(len);
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
122
Pods/leveldb-library/util/coding.h
generated
Normal file
122
Pods/leveldb-library/util/coding.h
generated
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_
|
||||
75
Pods/leveldb-library/util/comparator.cc
generated
Normal file
75
Pods/leveldb-library/util/comparator.cc
generated
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
Pods/leveldb-library/util/crc32c.cc
generated
Normal file
380
Pods/leveldb-library/util/crc32c.cc
generated
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
|
||||
43
Pods/leveldb-library/util/crc32c.h
generated
Normal file
43
Pods/leveldb-library/util/crc32c.h
generated
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_
|
||||
108
Pods/leveldb-library/util/env.cc
generated
Normal file
108
Pods/leveldb-library/util/env.cc
generated
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
|
||||
893
Pods/leveldb-library/util/env_posix.cc
generated
Normal file
893
Pods/leveldb-library/util/env_posix.cc
generated
Normal file
@@ -0,0 +1,893 @@
|
||||
// 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 <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <sys/mman.h>
|
||||
#include <sys/resource.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include <atomic>
|
||||
#include <cerrno>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
|
||||
#include "leveldb/env.h"
|
||||
#include "leveldb/slice.h"
|
||||
#include "leveldb/status.h"
|
||||
#include "port/port.h"
|
||||
#include "port/thread_annotations.h"
|
||||
#include "util/env_posix_test_helper.h"
|
||||
#include "util/posix_logger.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
namespace {
|
||||
|
||||
// Set by EnvPosixTestHelper::SetReadOnlyMMapLimit() and MaxOpenFiles().
|
||||
int g_open_read_only_file_limit = -1;
|
||||
|
||||
// Up to 1000 mmap regions for 64-bit binaries; none for 32-bit.
|
||||
constexpr const int kDefaultMmapLimit = (sizeof(void*) >= 8) ? 1000 : 0;
|
||||
|
||||
// Can be set using EnvPosixTestHelper::SetReadOnlyMMapLimit().
|
||||
int g_mmap_limit = kDefaultMmapLimit;
|
||||
|
||||
// Common flags defined for all posix open operations
|
||||
#if defined(HAVE_O_CLOEXEC)
|
||||
constexpr const int kOpenBaseFlags = O_CLOEXEC;
|
||||
#else
|
||||
constexpr const int kOpenBaseFlags = 0;
|
||||
#endif // defined(HAVE_O_CLOEXEC)
|
||||
|
||||
constexpr const size_t kWritableFileBufferSize = 65536;
|
||||
|
||||
Status PosixError(const std::string& context, int error_number) {
|
||||
if (error_number == ENOENT) {
|
||||
return Status::NotFound(context, std::strerror(error_number));
|
||||
} else {
|
||||
return Status::IOError(context, std::strerror(error_number));
|
||||
}
|
||||
}
|
||||
|
||||
// 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) : acquires_allowed_(max_acquires) {}
|
||||
|
||||
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() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); }
|
||||
|
||||
private:
|
||||
// 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_;
|
||||
};
|
||||
|
||||
// Implements sequential read access in a file using read().
|
||||
//
|
||||
// Instances of this class are thread-friendly but not thread-safe, as required
|
||||
// by the SequentialFile API.
|
||||
class PosixSequentialFile final : public SequentialFile {
|
||||
public:
|
||||
PosixSequentialFile(std::string filename, int fd)
|
||||
: fd_(fd), filename_(filename) {}
|
||||
~PosixSequentialFile() override { close(fd_); }
|
||||
|
||||
Status Read(size_t n, Slice* result, char* scratch) override {
|
||||
Status status;
|
||||
while (true) {
|
||||
::ssize_t read_size = ::read(fd_, scratch, n);
|
||||
if (read_size < 0) { // Read error.
|
||||
if (errno == EINTR) {
|
||||
continue; // Retry
|
||||
}
|
||||
status = PosixError(filename_, errno);
|
||||
break;
|
||||
}
|
||||
*result = Slice(scratch, read_size);
|
||||
break;
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status Skip(uint64_t n) override {
|
||||
if (::lseek(fd_, n, SEEK_CUR) == static_cast<off_t>(-1)) {
|
||||
return PosixError(filename_, errno);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
private:
|
||||
const int fd_;
|
||||
const std::string filename_;
|
||||
};
|
||||
|
||||
// Implements random read access in a file using pread().
|
||||
//
|
||||
// Instances of this class are thread-safe, as required by the RandomAccessFile
|
||||
// API. Instances are immutable and Read() only calls thread-safe library
|
||||
// functions.
|
||||
class PosixRandomAccessFile final : public RandomAccessFile {
|
||||
public:
|
||||
// The new instance takes ownership of |fd|. |fd_limiter| must outlive this
|
||||
// instance, and will be used to determine if .
|
||||
PosixRandomAccessFile(std::string filename, int fd, Limiter* fd_limiter)
|
||||
: has_permanent_fd_(fd_limiter->Acquire()),
|
||||
fd_(has_permanent_fd_ ? fd : -1),
|
||||
fd_limiter_(fd_limiter),
|
||||
filename_(std::move(filename)) {
|
||||
if (!has_permanent_fd_) {
|
||||
assert(fd_ == -1);
|
||||
::close(fd); // The file will be opened on every read.
|
||||
}
|
||||
}
|
||||
|
||||
~PosixRandomAccessFile() override {
|
||||
if (has_permanent_fd_) {
|
||||
assert(fd_ != -1);
|
||||
::close(fd_);
|
||||
fd_limiter_->Release();
|
||||
}
|
||||
}
|
||||
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
int fd = fd_;
|
||||
if (!has_permanent_fd_) {
|
||||
fd = ::open(filename_.c_str(), O_RDONLY | kOpenBaseFlags);
|
||||
if (fd < 0) {
|
||||
return PosixError(filename_, errno);
|
||||
}
|
||||
}
|
||||
|
||||
assert(fd != -1);
|
||||
|
||||
Status status;
|
||||
ssize_t read_size = ::pread(fd, scratch, n, static_cast<off_t>(offset));
|
||||
*result = Slice(scratch, (read_size < 0) ? 0 : read_size);
|
||||
if (read_size < 0) {
|
||||
// An error: return a non-ok status.
|
||||
status = PosixError(filename_, errno);
|
||||
}
|
||||
if (!has_permanent_fd_) {
|
||||
// Close the temporary file descriptor opened earlier.
|
||||
assert(fd != fd_);
|
||||
::close(fd);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
private:
|
||||
const bool has_permanent_fd_; // If false, the file is opened on every read.
|
||||
const int fd_; // -1 if has_permanent_fd_ is false.
|
||||
Limiter* const fd_limiter_;
|
||||
const std::string filename_;
|
||||
};
|
||||
|
||||
// Implements random read access in a file using mmap().
|
||||
//
|
||||
// Instances of this class are thread-safe, as required by the RandomAccessFile
|
||||
// API. Instances are immutable and Read() only calls thread-safe library
|
||||
// functions.
|
||||
class PosixMmapReadableFile final : public RandomAccessFile {
|
||||
public:
|
||||
// mmap_base[0, length-1] points to the memory-mapped contents of the file. It
|
||||
// must be the result of a successful call to mmap(). This instances takes
|
||||
// over the ownership of the region.
|
||||
//
|
||||
// |mmap_limiter| must outlive this instance. The caller must have already
|
||||
// aquired the right to use one mmap region, which will be released when this
|
||||
// instance is destroyed.
|
||||
PosixMmapReadableFile(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)) {}
|
||||
|
||||
~PosixMmapReadableFile() override {
|
||||
::munmap(static_cast<void*>(mmap_base_), length_);
|
||||
mmap_limiter_->Release();
|
||||
}
|
||||
|
||||
Status Read(uint64_t offset, size_t n, Slice* result,
|
||||
char* scratch) const override {
|
||||
if (offset + n > length_) {
|
||||
*result = Slice();
|
||||
return PosixError(filename_, EINVAL);
|
||||
}
|
||||
|
||||
*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 PosixWritableFile final : public WritableFile {
|
||||
public:
|
||||
PosixWritableFile(std::string filename, int fd)
|
||||
: pos_(0),
|
||||
fd_(fd),
|
||||
is_manifest_(IsManifest(filename)),
|
||||
filename_(std::move(filename)),
|
||||
dirname_(Dirname(filename_)) {}
|
||||
|
||||
~PosixWritableFile() override {
|
||||
if (fd_ >= 0) {
|
||||
// Ignoring any potential errors
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
const int close_result = ::close(fd_);
|
||||
if (close_result < 0 && status.ok()) {
|
||||
status = PosixError(filename_, errno);
|
||||
}
|
||||
fd_ = -1;
|
||||
return status;
|
||||
}
|
||||
|
||||
Status Flush() override { return FlushBuffer(); }
|
||||
|
||||
Status Sync() override {
|
||||
// Ensure new files referred to by the manifest are in the filesystem.
|
||||
//
|
||||
// This needs to happen before the manifest file is flushed to disk, to
|
||||
// avoid crashing in a state where the manifest refers to files that are not
|
||||
// yet on disk.
|
||||
Status status = SyncDirIfManifest();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
status = FlushBuffer();
|
||||
if (!status.ok()) {
|
||||
return status;
|
||||
}
|
||||
|
||||
return SyncFd(fd_, filename_);
|
||||
}
|
||||
|
||||
private:
|
||||
Status FlushBuffer() {
|
||||
Status status = WriteUnbuffered(buf_, pos_);
|
||||
pos_ = 0;
|
||||
return status;
|
||||
}
|
||||
|
||||
Status WriteUnbuffered(const char* data, size_t size) {
|
||||
while (size > 0) {
|
||||
ssize_t write_result = ::write(fd_, data, size);
|
||||
if (write_result < 0) {
|
||||
if (errno == EINTR) {
|
||||
continue; // Retry
|
||||
}
|
||||
return PosixError(filename_, errno);
|
||||
}
|
||||
data += write_result;
|
||||
size -= write_result;
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status SyncDirIfManifest() {
|
||||
Status status;
|
||||
if (!is_manifest_) {
|
||||
return status;
|
||||
}
|
||||
|
||||
int fd = ::open(dirname_.c_str(), O_RDONLY | kOpenBaseFlags);
|
||||
if (fd < 0) {
|
||||
status = PosixError(dirname_, errno);
|
||||
} else {
|
||||
status = SyncFd(fd, dirname_);
|
||||
::close(fd);
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
// Ensures that all the caches associated with the given file descriptor's
|
||||
// data are flushed all the way to durable media, and can withstand power
|
||||
// failures.
|
||||
//
|
||||
// The path argument is only used to populate the description string in the
|
||||
// returned Status if an error occurs.
|
||||
static Status SyncFd(int fd, const std::string& fd_path) {
|
||||
#if HAVE_FULLFSYNC
|
||||
// On macOS and iOS, fsync() doesn't guarantee durability past power
|
||||
// failures. fcntl(F_FULLFSYNC) is required for that purpose. Some
|
||||
// filesystems don't support fcntl(F_FULLFSYNC), and require a fallback to
|
||||
// fsync().
|
||||
if (::fcntl(fd, F_FULLFSYNC) == 0) {
|
||||
return Status::OK();
|
||||
}
|
||||
#endif // HAVE_FULLFSYNC
|
||||
|
||||
#if HAVE_FDATASYNC
|
||||
bool sync_success = ::fdatasync(fd) == 0;
|
||||
#else
|
||||
bool sync_success = ::fsync(fd) == 0;
|
||||
#endif // HAVE_FDATASYNC
|
||||
|
||||
if (sync_success) {
|
||||
return Status::OK();
|
||||
}
|
||||
return PosixError(fd_path, errno);
|
||||
}
|
||||
|
||||
// Returns the directory name in a path pointing to a file.
|
||||
//
|
||||
// Returns "." if the path does not contain any directory separator.
|
||||
static std::string Dirname(const std::string& filename) {
|
||||
std::string::size_type separator_pos = filename.rfind('/');
|
||||
if (separator_pos == std::string::npos) {
|
||||
return std::string(".");
|
||||
}
|
||||
// The filename component should not contain a path separator. If it does,
|
||||
// the splitting was done incorrectly.
|
||||
assert(filename.find('/', separator_pos + 1) == std::string::npos);
|
||||
|
||||
return filename.substr(0, separator_pos);
|
||||
}
|
||||
|
||||
// Extracts the file name from a path pointing to a file.
|
||||
//
|
||||
// The returned Slice points to |filename|'s data buffer, so it is only valid
|
||||
// while |filename| is alive and unchanged.
|
||||
static Slice Basename(const std::string& filename) {
|
||||
std::string::size_type separator_pos = filename.rfind('/');
|
||||
if (separator_pos == std::string::npos) {
|
||||
return Slice(filename);
|
||||
}
|
||||
// The filename component should not contain a path separator. If it does,
|
||||
// the splitting was done incorrectly.
|
||||
assert(filename.find('/', separator_pos + 1) == std::string::npos);
|
||||
|
||||
return Slice(filename.data() + separator_pos + 1,
|
||||
filename.length() - separator_pos - 1);
|
||||
}
|
||||
|
||||
// True if the given file is a manifest file.
|
||||
static bool IsManifest(const std::string& filename) {
|
||||
return Basename(filename).starts_with("MANIFEST");
|
||||
}
|
||||
|
||||
// buf_[0, pos_ - 1] contains data to be written to fd_.
|
||||
char buf_[kWritableFileBufferSize];
|
||||
size_t pos_;
|
||||
int fd_;
|
||||
|
||||
const bool is_manifest_; // True if the file's name starts with MANIFEST.
|
||||
const std::string filename_;
|
||||
const std::string dirname_; // The directory of filename_.
|
||||
};
|
||||
|
||||
int LockOrUnlock(int fd, bool lock) {
|
||||
errno = 0;
|
||||
struct ::flock file_lock_info;
|
||||
std::memset(&file_lock_info, 0, sizeof(file_lock_info));
|
||||
file_lock_info.l_type = (lock ? F_WRLCK : F_UNLCK);
|
||||
file_lock_info.l_whence = SEEK_SET;
|
||||
file_lock_info.l_start = 0;
|
||||
file_lock_info.l_len = 0; // Lock/unlock entire file.
|
||||
return ::fcntl(fd, F_SETLK, &file_lock_info);
|
||||
}
|
||||
|
||||
// Instances are thread-safe because they are immutable.
|
||||
class PosixFileLock : public FileLock {
|
||||
public:
|
||||
PosixFileLock(int fd, std::string filename)
|
||||
: fd_(fd), filename_(std::move(filename)) {}
|
||||
|
||||
int fd() const { return fd_; }
|
||||
const std::string& filename() const { return filename_; }
|
||||
|
||||
private:
|
||||
const int fd_;
|
||||
const std::string filename_;
|
||||
};
|
||||
|
||||
// Tracks the files locked by PosixEnv::LockFile().
|
||||
//
|
||||
// We maintain a separate set instead of relying on fcntl(F_SETLK) because
|
||||
// fcntl(F_SETLK) does not provide any protection against multiple uses from the
|
||||
// same process.
|
||||
//
|
||||
// Instances are thread-safe because all member data is guarded by a mutex.
|
||||
class PosixLockTable {
|
||||
public:
|
||||
bool Insert(const std::string& fname) LOCKS_EXCLUDED(mu_) {
|
||||
mu_.Lock();
|
||||
bool succeeded = locked_files_.insert(fname).second;
|
||||
mu_.Unlock();
|
||||
return succeeded;
|
||||
}
|
||||
void Remove(const std::string& fname) LOCKS_EXCLUDED(mu_) {
|
||||
mu_.Lock();
|
||||
locked_files_.erase(fname);
|
||||
mu_.Unlock();
|
||||
}
|
||||
|
||||
private:
|
||||
port::Mutex mu_;
|
||||
std::set<std::string> locked_files_ GUARDED_BY(mu_);
|
||||
};
|
||||
|
||||
class PosixEnv : public Env {
|
||||
public:
|
||||
PosixEnv();
|
||||
~PosixEnv() override {
|
||||
static const char msg[] =
|
||||
"PosixEnv singleton destroyed. Unsupported behavior!\n";
|
||||
std::fwrite(msg, 1, sizeof(msg), stderr);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
Status NewSequentialFile(const std::string& filename,
|
||||
SequentialFile** result) override {
|
||||
int fd = ::open(filename.c_str(), O_RDONLY | kOpenBaseFlags);
|
||||
if (fd < 0) {
|
||||
*result = nullptr;
|
||||
return PosixError(filename, errno);
|
||||
}
|
||||
|
||||
*result = new PosixSequentialFile(filename, fd);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewRandomAccessFile(const std::string& filename,
|
||||
RandomAccessFile** result) override {
|
||||
*result = nullptr;
|
||||
int fd = ::open(filename.c_str(), O_RDONLY | kOpenBaseFlags);
|
||||
if (fd < 0) {
|
||||
return PosixError(filename, errno);
|
||||
}
|
||||
|
||||
if (!mmap_limiter_.Acquire()) {
|
||||
*result = new PosixRandomAccessFile(filename, fd, &fd_limiter_);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
uint64_t file_size;
|
||||
Status status = GetFileSize(filename, &file_size);
|
||||
if (status.ok()) {
|
||||
void* mmap_base =
|
||||
::mmap(/*addr=*/nullptr, file_size, PROT_READ, MAP_SHARED, fd, 0);
|
||||
if (mmap_base != MAP_FAILED) {
|
||||
*result = new PosixMmapReadableFile(filename,
|
||||
reinterpret_cast<char*>(mmap_base),
|
||||
file_size, &mmap_limiter_);
|
||||
} else {
|
||||
status = PosixError(filename, errno);
|
||||
}
|
||||
}
|
||||
::close(fd);
|
||||
if (!status.ok()) {
|
||||
mmap_limiter_.Release();
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
Status NewWritableFile(const std::string& filename,
|
||||
WritableFile** result) override {
|
||||
int fd = ::open(filename.c_str(),
|
||||
O_TRUNC | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644);
|
||||
if (fd < 0) {
|
||||
*result = nullptr;
|
||||
return PosixError(filename, errno);
|
||||
}
|
||||
|
||||
*result = new PosixWritableFile(filename, fd);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewAppendableFile(const std::string& filename,
|
||||
WritableFile** result) override {
|
||||
int fd = ::open(filename.c_str(),
|
||||
O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644);
|
||||
if (fd < 0) {
|
||||
*result = nullptr;
|
||||
return PosixError(filename, errno);
|
||||
}
|
||||
|
||||
*result = new PosixWritableFile(filename, fd);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
bool FileExists(const std::string& filename) override {
|
||||
return ::access(filename.c_str(), F_OK) == 0;
|
||||
}
|
||||
|
||||
Status GetChildren(const std::string& directory_path,
|
||||
std::vector<std::string>* result) override {
|
||||
result->clear();
|
||||
::DIR* dir = ::opendir(directory_path.c_str());
|
||||
if (dir == nullptr) {
|
||||
return PosixError(directory_path, errno);
|
||||
}
|
||||
struct ::dirent* entry;
|
||||
while ((entry = ::readdir(dir)) != nullptr) {
|
||||
result->emplace_back(entry->d_name);
|
||||
}
|
||||
::closedir(dir);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status RemoveFile(const std::string& filename) override {
|
||||
if (::unlink(filename.c_str()) != 0) {
|
||||
return PosixError(filename, errno);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status CreateDir(const std::string& dirname) override {
|
||||
if (::mkdir(dirname.c_str(), 0755) != 0) {
|
||||
return PosixError(dirname, errno);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status RemoveDir(const std::string& dirname) override {
|
||||
if (::rmdir(dirname.c_str()) != 0) {
|
||||
return PosixError(dirname, errno);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status GetFileSize(const std::string& filename, uint64_t* size) override {
|
||||
struct ::stat file_stat;
|
||||
if (::stat(filename.c_str(), &file_stat) != 0) {
|
||||
*size = 0;
|
||||
return PosixError(filename, errno);
|
||||
}
|
||||
*size = file_stat.st_size;
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status RenameFile(const std::string& from, const std::string& to) override {
|
||||
if (std::rename(from.c_str(), to.c_str()) != 0) {
|
||||
return PosixError(from, errno);
|
||||
}
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status LockFile(const std::string& filename, FileLock** lock) override {
|
||||
*lock = nullptr;
|
||||
|
||||
int fd = ::open(filename.c_str(), O_RDWR | O_CREAT | kOpenBaseFlags, 0644);
|
||||
if (fd < 0) {
|
||||
return PosixError(filename, errno);
|
||||
}
|
||||
|
||||
if (!locks_.Insert(filename)) {
|
||||
::close(fd);
|
||||
return Status::IOError("lock " + filename, "already held by process");
|
||||
}
|
||||
|
||||
if (LockOrUnlock(fd, true) == -1) {
|
||||
int lock_errno = errno;
|
||||
::close(fd);
|
||||
locks_.Remove(filename);
|
||||
return PosixError("lock " + filename, lock_errno);
|
||||
}
|
||||
|
||||
*lock = new PosixFileLock(fd, filename);
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status UnlockFile(FileLock* lock) override {
|
||||
PosixFileLock* posix_file_lock = static_cast<PosixFileLock*>(lock);
|
||||
if (LockOrUnlock(posix_file_lock->fd(), false) == -1) {
|
||||
return PosixError("unlock " + posix_file_lock->filename(), errno);
|
||||
}
|
||||
locks_.Remove(posix_file_lock->filename());
|
||||
::close(posix_file_lock->fd());
|
||||
delete posix_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 = std::getenv("TEST_TMPDIR");
|
||||
if (env && env[0] != '\0') {
|
||||
*result = env;
|
||||
} else {
|
||||
char buf[100];
|
||||
std::snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d",
|
||||
static_cast<int>(::geteuid()));
|
||||
*result = buf;
|
||||
}
|
||||
|
||||
// The CreateDir status is ignored because the directory may already exist.
|
||||
CreateDir(*result);
|
||||
|
||||
return Status::OK();
|
||||
}
|
||||
|
||||
Status NewLogger(const std::string& filename, Logger** result) override {
|
||||
int fd = ::open(filename.c_str(),
|
||||
O_APPEND | O_WRONLY | O_CREAT | kOpenBaseFlags, 0644);
|
||||
if (fd < 0) {
|
||||
*result = nullptr;
|
||||
return PosixError(filename, errno);
|
||||
}
|
||||
|
||||
std::FILE* fp = ::fdopen(fd, "w");
|
||||
if (fp == nullptr) {
|
||||
::close(fd);
|
||||
*result = nullptr;
|
||||
return PosixError(filename, errno);
|
||||
} else {
|
||||
*result = new PosixLogger(fp);
|
||||
return Status::OK();
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t NowMicros() override {
|
||||
static constexpr uint64_t kUsecondsPerSecond = 1000000;
|
||||
struct ::timeval tv;
|
||||
::gettimeofday(&tv, nullptr);
|
||||
return static_cast<uint64_t>(tv.tv_sec) * kUsecondsPerSecond + tv.tv_usec;
|
||||
}
|
||||
|
||||
void SleepForMicroseconds(int micros) override {
|
||||
std::this_thread::sleep_for(std::chrono::microseconds(micros));
|
||||
}
|
||||
|
||||
private:
|
||||
void BackgroundThreadMain();
|
||||
|
||||
static void BackgroundThreadEntryPoint(PosixEnv* 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 beacuse 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_);
|
||||
|
||||
PosixLockTable locks_; // Thread-safe.
|
||||
Limiter mmap_limiter_; // Thread-safe.
|
||||
Limiter fd_limiter_; // Thread-safe.
|
||||
};
|
||||
|
||||
// Return the maximum number of concurrent mmaps.
|
||||
int MaxMmaps() { return g_mmap_limit; }
|
||||
|
||||
// Return the maximum number of read-only files to keep open.
|
||||
int MaxOpenFiles() {
|
||||
if (g_open_read_only_file_limit >= 0) {
|
||||
return g_open_read_only_file_limit;
|
||||
}
|
||||
struct ::rlimit rlim;
|
||||
if (::getrlimit(RLIMIT_NOFILE, &rlim)) {
|
||||
// getrlimit failed, fallback to hard-coded default.
|
||||
g_open_read_only_file_limit = 50;
|
||||
} else if (rlim.rlim_cur == RLIM_INFINITY) {
|
||||
g_open_read_only_file_limit = std::numeric_limits<int>::max();
|
||||
} else {
|
||||
// Allow use of 20% of available file descriptors for read-only files.
|
||||
g_open_read_only_file_limit = (uint32_t)rlim.rlim_cur / 5;
|
||||
}
|
||||
return g_open_read_only_file_limit;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
PosixEnv::PosixEnv()
|
||||
: background_work_cv_(&background_work_mutex_),
|
||||
started_background_thread_(false),
|
||||
mmap_limiter_(MaxMmaps()),
|
||||
fd_limiter_(MaxOpenFiles()) {}
|
||||
|
||||
void PosixEnv::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(PosixEnv::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 PosixEnv::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);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// 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::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::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 PosixDefaultEnv = SingletonEnv<PosixEnv>;
|
||||
|
||||
} // namespace
|
||||
|
||||
void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) {
|
||||
PosixDefaultEnv::AssertEnvNotInitialized();
|
||||
g_open_read_only_file_limit = limit;
|
||||
}
|
||||
|
||||
void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) {
|
||||
PosixDefaultEnv::AssertEnvNotInitialized();
|
||||
g_mmap_limit = limit;
|
||||
}
|
||||
|
||||
Env* Env::Default() {
|
||||
static PosixDefaultEnv env_container;
|
||||
return env_container.env();
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
28
Pods/leveldb-library/util/env_posix_test_helper.h
generated
Normal file
28
Pods/leveldb-library/util/env_posix_test_helper.h
generated
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
Pods/leveldb-library/util/env_windows_test_helper.h
generated
Normal file
25
Pods/leveldb-library/util/env_windows_test_helper.h
generated
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_
|
||||
11
Pods/leveldb-library/util/filter_policy.cc
generated
Normal file
11
Pods/leveldb-library/util/filter_policy.cc
generated
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
|
||||
55
Pods/leveldb-library/util/hash.cc
generated
Normal file
55
Pods/leveldb-library/util/hash.cc
generated
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 ^ ((uint32_t)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
|
||||
19
Pods/leveldb-library/util/hash.h
generated
Normal file
19
Pods/leveldb-library/util/hash.h
generated
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_
|
||||
272
Pods/leveldb-library/util/histogram.cc
generated
Normal file
272
Pods/leveldb-library/util/histogram.cc
generated
Normal file
@@ -0,0 +1,272 @@
|
||||
// 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/histogram.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdio>
|
||||
|
||||
#include "port/port.h"
|
||||
|
||||
namespace leveldb {
|
||||
|
||||
const double Histogram::kBucketLimit[kNumBuckets] = {
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10,
|
||||
12,
|
||||
14,
|
||||
16,
|
||||
18,
|
||||
20,
|
||||
25,
|
||||
30,
|
||||
35,
|
||||
40,
|
||||
45,
|
||||
50,
|
||||
60,
|
||||
70,
|
||||
80,
|
||||
90,
|
||||
100,
|
||||
120,
|
||||
140,
|
||||
160,
|
||||
180,
|
||||
200,
|
||||
250,
|
||||
300,
|
||||
350,
|
||||
400,
|
||||
450,
|
||||
500,
|
||||
600,
|
||||
700,
|
||||
800,
|
||||
900,
|
||||
1000,
|
||||
1200,
|
||||
1400,
|
||||
1600,
|
||||
1800,
|
||||
2000,
|
||||
2500,
|
||||
3000,
|
||||
3500,
|
||||
4000,
|
||||
4500,
|
||||
5000,
|
||||
6000,
|
||||
7000,
|
||||
8000,
|
||||
9000,
|
||||
10000,
|
||||
12000,
|
||||
14000,
|
||||
16000,
|
||||
18000,
|
||||
20000,
|
||||
25000,
|
||||
30000,
|
||||
35000,
|
||||
40000,
|
||||
45000,
|
||||
50000,
|
||||
60000,
|
||||
70000,
|
||||
80000,
|
||||
90000,
|
||||
100000,
|
||||
120000,
|
||||
140000,
|
||||
160000,
|
||||
180000,
|
||||
200000,
|
||||
250000,
|
||||
300000,
|
||||
350000,
|
||||
400000,
|
||||
450000,
|
||||
500000,
|
||||
600000,
|
||||
700000,
|
||||
800000,
|
||||
900000,
|
||||
1000000,
|
||||
1200000,
|
||||
1400000,
|
||||
1600000,
|
||||
1800000,
|
||||
2000000,
|
||||
2500000,
|
||||
3000000,
|
||||
3500000,
|
||||
4000000,
|
||||
4500000,
|
||||
5000000,
|
||||
6000000,
|
||||
7000000,
|
||||
8000000,
|
||||
9000000,
|
||||
10000000,
|
||||
12000000,
|
||||
14000000,
|
||||
16000000,
|
||||
18000000,
|
||||
20000000,
|
||||
25000000,
|
||||
30000000,
|
||||
35000000,
|
||||
40000000,
|
||||
45000000,
|
||||
50000000,
|
||||
60000000,
|
||||
70000000,
|
||||
80000000,
|
||||
90000000,
|
||||
100000000,
|
||||
120000000,
|
||||
140000000,
|
||||
160000000,
|
||||
180000000,
|
||||
200000000,
|
||||
250000000,
|
||||
300000000,
|
||||
350000000,
|
||||
400000000,
|
||||
450000000,
|
||||
500000000,
|
||||
600000000,
|
||||
700000000,
|
||||
800000000,
|
||||
900000000,
|
||||
1000000000,
|
||||
1200000000,
|
||||
1400000000,
|
||||
1600000000,
|
||||
1800000000,
|
||||
2000000000,
|
||||
2500000000.0,
|
||||
3000000000.0,
|
||||
3500000000.0,
|
||||
4000000000.0,
|
||||
4500000000.0,
|
||||
5000000000.0,
|
||||
6000000000.0,
|
||||
7000000000.0,
|
||||
8000000000.0,
|
||||
9000000000.0,
|
||||
1e200,
|
||||
};
|
||||
|
||||
void Histogram::Clear() {
|
||||
min_ = kBucketLimit[kNumBuckets - 1];
|
||||
max_ = 0;
|
||||
num_ = 0;
|
||||
sum_ = 0;
|
||||
sum_squares_ = 0;
|
||||
for (int i = 0; i < kNumBuckets; i++) {
|
||||
buckets_[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void Histogram::Add(double value) {
|
||||
// Linear search is fast enough for our usage in db_bench
|
||||
int b = 0;
|
||||
while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) {
|
||||
b++;
|
||||
}
|
||||
buckets_[b] += 1.0;
|
||||
if (min_ > value) min_ = value;
|
||||
if (max_ < value) max_ = value;
|
||||
num_++;
|
||||
sum_ += value;
|
||||
sum_squares_ += (value * value);
|
||||
}
|
||||
|
||||
void Histogram::Merge(const Histogram& other) {
|
||||
if (other.min_ < min_) min_ = other.min_;
|
||||
if (other.max_ > max_) max_ = other.max_;
|
||||
num_ += other.num_;
|
||||
sum_ += other.sum_;
|
||||
sum_squares_ += other.sum_squares_;
|
||||
for (int b = 0; b < kNumBuckets; b++) {
|
||||
buckets_[b] += other.buckets_[b];
|
||||
}
|
||||
}
|
||||
|
||||
double Histogram::Median() const { return Percentile(50.0); }
|
||||
|
||||
double Histogram::Percentile(double p) const {
|
||||
double threshold = num_ * (p / 100.0);
|
||||
double sum = 0;
|
||||
for (int b = 0; b < kNumBuckets; b++) {
|
||||
sum += buckets_[b];
|
||||
if (sum >= threshold) {
|
||||
// Scale linearly within this bucket
|
||||
double left_point = (b == 0) ? 0 : kBucketLimit[b - 1];
|
||||
double right_point = kBucketLimit[b];
|
||||
double left_sum = sum - buckets_[b];
|
||||
double right_sum = sum;
|
||||
double pos = (threshold - left_sum) / (right_sum - left_sum);
|
||||
double r = left_point + (right_point - left_point) * pos;
|
||||
if (r < min_) r = min_;
|
||||
if (r > max_) r = max_;
|
||||
return r;
|
||||
}
|
||||
}
|
||||
return max_;
|
||||
}
|
||||
|
||||
double Histogram::Average() const {
|
||||
if (num_ == 0.0) return 0;
|
||||
return sum_ / num_;
|
||||
}
|
||||
|
||||
double Histogram::StandardDeviation() const {
|
||||
if (num_ == 0.0) return 0;
|
||||
double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_);
|
||||
return sqrt(variance);
|
||||
}
|
||||
|
||||
std::string Histogram::ToString() const {
|
||||
std::string r;
|
||||
char buf[200];
|
||||
std::snprintf(buf, sizeof(buf), "Count: %.0f Average: %.4f StdDev: %.2f\n",
|
||||
num_, Average(), StandardDeviation());
|
||||
r.append(buf);
|
||||
std::snprintf(buf, sizeof(buf), "Min: %.4f Median: %.4f Max: %.4f\n",
|
||||
(num_ == 0.0 ? 0.0 : min_), Median(), max_);
|
||||
r.append(buf);
|
||||
r.append("------------------------------------------------------\n");
|
||||
const double mult = 100.0 / num_;
|
||||
double sum = 0;
|
||||
for (int b = 0; b < kNumBuckets; b++) {
|
||||
if (buckets_[b] <= 0.0) continue;
|
||||
sum += buckets_[b];
|
||||
std::snprintf(buf, sizeof(buf), "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ",
|
||||
((b == 0) ? 0.0 : kBucketLimit[b - 1]), // left
|
||||
kBucketLimit[b], // right
|
||||
buckets_[b], // count
|
||||
mult * buckets_[b], // percentage
|
||||
mult * sum); // cumulative percentage
|
||||
r.append(buf);
|
||||
|
||||
// Add hash marks based on percentage; 20 marks for 100%.
|
||||
int marks = static_cast<int>(20 * (buckets_[b] / num_) + 0.5);
|
||||
r.append(marks, '#');
|
||||
r.push_back('\n');
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
} // namespace leveldb
|
||||
44
Pods/leveldb-library/util/histogram.h
generated
Normal file
44
Pods/leveldb-library/util/histogram.h
generated
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_
|
||||
82
Pods/leveldb-library/util/logging.cc
generated
Normal file
82
Pods/leveldb-library/util/logging.cc
generated
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
|
||||
44
Pods/leveldb-library/util/logging.h
generated
Normal file
44
Pods/leveldb-library/util/logging.h
generated
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
Pods/leveldb-library/util/mutexlock.h
generated
Normal file
39
Pods/leveldb-library/util/mutexlock.h
generated
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
Pods/leveldb-library/util/no_destructor.h
generated
Normal file
46
Pods/leveldb-library/util/no_destructor.h
generated
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_
|
||||
14
Pods/leveldb-library/util/options.cc
generated
Normal file
14
Pods/leveldb-library/util/options.cc
generated
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
|
||||
130
Pods/leveldb-library/util/posix_logger.h
generated
Normal file
130
Pods/leveldb-library/util/posix_logger.h
generated
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
Pods/leveldb-library/util/random.h
generated
Normal file
63
Pods/leveldb-library/util/random.h
generated
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_
|
||||
77
Pods/leveldb-library/util/status.cc
generated
Normal file
77
Pods/leveldb-library/util/status.cc
generated
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
|
||||
82
Pods/leveldb-library/util/testutil.h
generated
Normal file
82
Pods/leveldb-library/util/testutil.h
generated
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
|
||||
// util::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
Pods/leveldb-library/util/windows_logger.h
generated
Normal file
124
Pods/leveldb-library/util/windows_logger.h
generated
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_
|
||||
Reference in New Issue
Block a user