summaryrefslogtreecommitdiff
path: root/util
diff options
context:
space:
mode:
authorVictor Costan <costan@google.com>2022-01-09 23:08:24 -0800
committerGitHub <noreply@github.com>2022-01-09 23:08:24 -0800
commit3180f9cb402f51ce89be7011b103747bc6462976 (patch)
tree9671501a3176d0f9fc58284b6455a36b4f4986b1 /util
parentf933ad16934098460622bbc62d17761802486393 (diff)
parent8ccb79b57e877cb28b751d74e3e50bb4f8184997 (diff)
downloadleveldb-3180f9cb402f51ce89be7011b103747bc6462976.tar.gz
Merge branch 'master' into patch-1
Diffstat (limited to 'util')
-rw-r--r--util/arena_test.cc6
-rw-r--r--util/bloom_test.cc28
-rw-r--r--util/cache.cc11
-rw-r--r--util/cache_test.cc28
-rw-r--r--util/coding.h50
-rw-r--r--util/coding_test.cc9
-rw-r--r--util/crc32c.cc4
-rw-r--r--util/crc32c.h4
-rw-r--r--util/crc32c_test.cc7
-rw-r--r--util/env.cc20
-rw-r--r--util/env_posix.cc55
-rw-r--r--util/env_posix_test.cc93
-rw-r--r--util/env_test.cc78
-rw-r--r--util/env_windows.cc40
-rw-r--r--util/env_windows_test.cc23
-rw-r--r--util/hash.cc2
-rw-r--r--util/hash.h4
-rw-r--r--util/hash_test.cc7
-rw-r--r--util/histogram.cc24
-rw-r--r--util/logging.cc14
-rw-r--r--util/logging.h5
-rw-r--r--util/logging_test.cc9
-rw-r--r--util/no_destructor_test.cc9
-rw-r--r--util/posix_logger.h8
-rw-r--r--util/random.h2
-rw-r--r--util/status.cc18
-rw-r--r--util/status_test.cc7
-rw-r--r--util/testharness.cc81
-rw-r--r--util/testharness.h141
-rw-r--r--util/testutil.cc2
-rw-r--r--util/testutil.h16
-rw-r--r--util/windows_logger.h8
32 files changed, 304 insertions, 509 deletions
diff --git a/util/arena_test.cc b/util/arena_test.cc
index e917228..3e2011e 100644
--- a/util/arena_test.cc
+++ b/util/arena_test.cc
@@ -4,13 +4,11 @@
#include "util/arena.h"
+#include "gtest/gtest.h"
#include "util/random.h"
-#include "util/testharness.h"
namespace leveldb {
-class ArenaTest {};
-
TEST(ArenaTest, Empty) { Arena arena; }
TEST(ArenaTest, Simple) {
@@ -61,5 +59,3 @@ TEST(ArenaTest, Simple) {
}
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/bloom_test.cc b/util/bloom_test.cc
index 436daa9..9f11108 100644
--- a/util/bloom_test.cc
+++ b/util/bloom_test.cc
@@ -2,11 +2,10 @@
// 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 "gtest/gtest.h"
#include "leveldb/filter_policy.h"
-
#include "util/coding.h"
#include "util/logging.h"
-#include "util/testharness.h"
#include "util/testutil.h"
namespace leveldb {
@@ -18,7 +17,7 @@ static Slice Key(int i, char* buffer) {
return Slice(buffer, sizeof(uint32_t));
}
-class BloomTest {
+class BloomTest : public testing::Test {
public:
BloomTest() : policy_(NewBloomFilterPolicy(10)) {}
@@ -46,14 +45,14 @@ class BloomTest {
size_t FilterSize() const { return filter_.size(); }
void DumpFilter() {
- fprintf(stderr, "F(");
+ std::fprintf(stderr, "F(");
for (size_t i = 0; i + 1 < filter_.size(); i++) {
const unsigned int c = static_cast<unsigned int>(filter_[i]);
for (int j = 0; j < 8; j++) {
- fprintf(stderr, "%c", (c & (1 << j)) ? '1' : '.');
+ std::fprintf(stderr, "%c", (c & (1 << j)) ? '1' : '.');
}
}
- fprintf(stderr, ")\n");
+ std::fprintf(stderr, ")\n");
}
bool Matches(const Slice& s) {
@@ -80,12 +79,12 @@ class BloomTest {
std::vector<std::string> keys_;
};
-TEST(BloomTest, EmptyFilter) {
+TEST_F(BloomTest, EmptyFilter) {
ASSERT_TRUE(!Matches("hello"));
ASSERT_TRUE(!Matches("world"));
}
-TEST(BloomTest, Small) {
+TEST_F(BloomTest, Small) {
Add("hello");
Add("world");
ASSERT_TRUE(Matches("hello"));
@@ -107,7 +106,7 @@ static int NextLength(int length) {
return length;
}
-TEST(BloomTest, VaryingLengths) {
+TEST_F(BloomTest, VaryingLengths) {
char buffer[sizeof(int)];
// Count number of filters that significantly exceed the false positive rate
@@ -133,8 +132,9 @@ TEST(BloomTest, VaryingLengths) {
// Check false positive rate
double rate = FalsePositiveRate();
if (kVerbose >= 1) {
- fprintf(stderr, "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n",
- rate * 100.0, length, static_cast<int>(FilterSize()));
+ std::fprintf(stderr,
+ "False positives: %5.2f%% @ length = %6d ; bytes = %6d\n",
+ rate * 100.0, length, static_cast<int>(FilterSize()));
}
ASSERT_LE(rate, 0.02); // Must not be over 2%
if (rate > 0.0125)
@@ -143,8 +143,8 @@ TEST(BloomTest, VaryingLengths) {
good_filters++;
}
if (kVerbose >= 1) {
- fprintf(stderr, "Filters: %d good, %d mediocre\n", good_filters,
- mediocre_filters);
+ std::fprintf(stderr, "Filters: %d good, %d mediocre\n", good_filters,
+ mediocre_filters);
}
ASSERT_LE(mediocre_filters, good_filters / 5);
}
@@ -152,5 +152,3 @@ TEST(BloomTest, VaryingLengths) {
// Different bits-per-byte
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/cache.cc b/util/cache.cc
index 12de306..ad1e9a2 100644
--- a/util/cache.cc
+++ b/util/cache.cc
@@ -2,11 +2,12 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
-#include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
-
#include "leveldb/cache.h"
+
+#include <cassert>
+#include <cstdio>
+#include <cstdlib>
+
#include "port/port.h"
#include "port/thread_annotations.h"
#include "util/hash.h"
@@ -278,7 +279,7 @@ Cache::Handle* LRUCache::Insert(const Slice& key, uint32_t hash, void* value,
e->hash = hash;
e->in_cache = false;
e->refs = 1; // for the returned handle.
- memcpy(e->key_data, key.data(), key.size());
+ std::memcpy(e->key_data, key.data(), key.size());
if (capacity_ > 0) {
e->refs++; // for the cache's reference.
diff --git a/util/cache_test.cc b/util/cache_test.cc
index 974334b..e68da34 100644
--- a/util/cache_test.cc
+++ b/util/cache_test.cc
@@ -5,8 +5,9 @@
#include "leveldb/cache.h"
#include <vector>
+
+#include "gtest/gtest.h"
#include "util/coding.h"
-#include "util/testharness.h"
namespace leveldb {
@@ -23,14 +24,14 @@ static int DecodeKey(const Slice& k) {
static void* EncodeValue(uintptr_t v) { return reinterpret_cast<void*>(v); }
static int DecodeValue(void* v) { return reinterpret_cast<uintptr_t>(v); }
-class CacheTest {
+class CacheTest : public testing::Test {
public:
static void Deleter(const Slice& key, void* v) {
current_->deleted_keys_.push_back(DecodeKey(key));
current_->deleted_values_.push_back(DecodeValue(v));
}
- static const int kCacheSize = 1000;
+ static constexpr int kCacheSize = 1000;
std::vector<int> deleted_keys_;
std::vector<int> deleted_values_;
Cache* cache_;
@@ -59,12 +60,11 @@ class CacheTest {
}
void Erase(int key) { cache_->Erase(EncodeKey(key)); }
-
static CacheTest* current_;
};
CacheTest* CacheTest::current_;
-TEST(CacheTest, HitAndMiss) {
+TEST_F(CacheTest, HitAndMiss) {
ASSERT_EQ(-1, Lookup(100));
Insert(100, 101);
@@ -87,7 +87,7 @@ TEST(CacheTest, HitAndMiss) {
ASSERT_EQ(101, deleted_values_[0]);
}
-TEST(CacheTest, Erase) {
+TEST_F(CacheTest, Erase) {
Erase(200);
ASSERT_EQ(0, deleted_keys_.size());
@@ -106,7 +106,7 @@ TEST(CacheTest, Erase) {
ASSERT_EQ(1, deleted_keys_.size());
}
-TEST(CacheTest, EntriesArePinned) {
+TEST_F(CacheTest, EntriesArePinned) {
Insert(100, 101);
Cache::Handle* h1 = cache_->Lookup(EncodeKey(100));
ASSERT_EQ(101, DecodeValue(cache_->Value(h1)));
@@ -131,7 +131,7 @@ TEST(CacheTest, EntriesArePinned) {
ASSERT_EQ(102, deleted_values_[1]);
}
-TEST(CacheTest, EvictionPolicy) {
+TEST_F(CacheTest, EvictionPolicy) {
Insert(100, 101);
Insert(200, 201);
Insert(300, 301);
@@ -150,7 +150,7 @@ TEST(CacheTest, EvictionPolicy) {
cache_->Release(h);
}
-TEST(CacheTest, UseExceedsCacheSize) {
+TEST_F(CacheTest, UseExceedsCacheSize) {
// Overfill the cache, keeping handles on all inserted entries.
std::vector<Cache::Handle*> h;
for (int i = 0; i < kCacheSize + 100; i++) {
@@ -167,7 +167,7 @@ TEST(CacheTest, UseExceedsCacheSize) {
}
}
-TEST(CacheTest, HeavyEntries) {
+TEST_F(CacheTest, HeavyEntries) {
// Add a bunch of light and heavy entries and then count the combined
// size of items still in the cache, which must be approximately the
// same as the total capacity.
@@ -194,13 +194,13 @@ TEST(CacheTest, HeavyEntries) {
ASSERT_LE(cached_weight, kCacheSize + kCacheSize / 10);
}
-TEST(CacheTest, NewId) {
+TEST_F(CacheTest, NewId) {
uint64_t a = cache_->NewId();
uint64_t b = cache_->NewId();
ASSERT_NE(a, b);
}
-TEST(CacheTest, Prune) {
+TEST_F(CacheTest, Prune) {
Insert(1, 100);
Insert(2, 200);
@@ -213,7 +213,7 @@ TEST(CacheTest, Prune) {
ASSERT_EQ(-1, Lookup(2));
}
-TEST(CacheTest, ZeroSizeCache) {
+TEST_F(CacheTest, ZeroSizeCache) {
delete cache_;
cache_ = NewLRUCache(0);
@@ -222,5 +222,3 @@ TEST(CacheTest, ZeroSizeCache) {
}
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/coding.h b/util/coding.h
index 1983ae7..f0bb57b 100644
--- a/util/coding.h
+++ b/util/coding.h
@@ -48,29 +48,13 @@ int VarintLength(uint64_t v);
char* EncodeVarint32(char* dst, uint32_t value);
char* EncodeVarint64(char* dst, uint64_t value);
-// TODO(costan): Remove port::kLittleEndian and the fast paths based on
-// std::memcpy when clang learns to optimize the generic code, as
-// described in https://bugs.llvm.org/show_bug.cgi?id=41761
-//
-// The platform-independent code in DecodeFixed{32,64}() gets optimized to mov
-// on x86 and ldr on ARM64, by both clang and gcc. However, only gcc optimizes
-// the platform-independent code in EncodeFixed{32,64}() to mov / str.
-
// 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);
- if (port::kLittleEndian) {
- // Fast path for little-endian CPUs. All major compilers optimize this to a
- // single mov (x86_64) / str (ARM) instruction.
- std::memcpy(buffer, &value, sizeof(uint32_t));
- return;
- }
-
- // Platform-independent code.
- // Currently, only gcc optimizes this to a single mov / str instruction.
+ // 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);
@@ -80,15 +64,7 @@ inline void EncodeFixed32(char* dst, uint32_t value) {
inline void EncodeFixed64(char* dst, uint64_t value) {
uint8_t* const buffer = reinterpret_cast<uint8_t*>(dst);
- if (port::kLittleEndian) {
- // Fast path for little-endian CPUs. All major compilers optimize this to a
- // single mov (x86_64) / str (ARM) instruction.
- std::memcpy(buffer, &value, sizeof(uint64_t));
- return;
- }
-
- // Platform-independent code.
- // Currently, only gcc optimizes this to a single mov / str instruction.
+ // 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);
@@ -105,16 +81,7 @@ inline void EncodeFixed64(char* dst, uint64_t value) {
inline uint32_t DecodeFixed32(const char* ptr) {
const uint8_t* const buffer = reinterpret_cast<const uint8_t*>(ptr);
- if (port::kLittleEndian) {
- // Fast path for little-endian CPUs. All major compilers optimize this to a
- // single mov (x86_64) / ldr (ARM) instruction.
- uint32_t result;
- std::memcpy(&result, buffer, sizeof(uint32_t));
- return result;
- }
-
- // Platform-independent code.
- // Clang and gcc optimize this to a single mov / ldr instruction.
+ // 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) |
@@ -124,16 +91,7 @@ inline uint32_t DecodeFixed32(const char* ptr) {
inline uint64_t DecodeFixed64(const char* ptr) {
const uint8_t* const buffer = reinterpret_cast<const uint8_t*>(ptr);
- if (port::kLittleEndian) {
- // Fast path for little-endian CPUs. All major compilers optimize this to a
- // single mov (x86_64) / ldr (ARM) instruction.
- uint64_t result;
- std::memcpy(&result, buffer, sizeof(uint64_t));
- return result;
- }
-
- // Platform-independent code.
- // Clang and gcc optimize this to a single mov / ldr instruction.
+ // 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) |
diff --git a/util/coding_test.cc b/util/coding_test.cc
index 0d2a0c5..cceda14 100644
--- a/util/coding_test.cc
+++ b/util/coding_test.cc
@@ -2,15 +2,14 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.
+#include "util/coding.h"
+
#include <vector>
-#include "util/coding.h"
-#include "util/testharness.h"
+#include "gtest/gtest.h"
namespace leveldb {
-class Coding {};
-
TEST(Coding, Fixed32) {
std::string s;
for (uint32_t v = 0; v < 100000; v++) {
@@ -192,5 +191,3 @@ TEST(Coding, Strings) {
}
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/crc32c.cc b/util/crc32c.cc
index c2e61f7..3f18908 100644
--- a/util/crc32c.cc
+++ b/util/crc32c.cc
@@ -6,8 +6,8 @@
#include "util/crc32c.h"
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
#include "port/port.h"
#include "util/coding.h"
diff --git a/util/crc32c.h b/util/crc32c.h
index 98fabb0..b420b5f 100644
--- a/util/crc32c.h
+++ b/util/crc32c.h
@@ -5,8 +5,8 @@
#ifndef STORAGE_LEVELDB_UTIL_CRC32C_H_
#define STORAGE_LEVELDB_UTIL_CRC32C_H_
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
namespace leveldb {
namespace crc32c {
diff --git a/util/crc32c_test.cc b/util/crc32c_test.cc
index 18a8494..2fe1c41 100644
--- a/util/crc32c_test.cc
+++ b/util/crc32c_test.cc
@@ -3,13 +3,12 @@
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "util/crc32c.h"
-#include "util/testharness.h"
+
+#include "gtest/gtest.h"
namespace leveldb {
namespace crc32c {
-class CRC {};
-
TEST(CRC, StandardResults) {
// From rfc3720 section B.4.
char buf[32];
@@ -55,5 +54,3 @@ TEST(CRC, Mask) {
} // namespace crc32c
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/env.cc b/util/env.cc
index d2f0aef..a53b230 100644
--- a/util/env.cc
+++ b/util/env.cc
@@ -4,14 +4,30 @@
#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;
@@ -24,7 +40,7 @@ FileLock::~FileLock() = default;
void Log(Logger* info_log, const char* format, ...) {
if (info_log != nullptr) {
- va_list ap;
+ std::va_list ap;
va_start(ap, format);
info_log->Logv(format, ap);
va_end(ap);
@@ -47,7 +63,7 @@ static Status DoWriteStringToFile(Env* env, const Slice& data,
}
delete file; // Will auto-close if we did not close above
if (!s.ok()) {
- env->DeleteFile(fname);
+ env->RemoveFile(fname);
}
return s;
}
diff --git a/util/env_posix.cc b/util/env_posix.cc
index 00ca9ae..ffd06c4 100644
--- a/util/env_posix.cc
+++ b/util/env_posix.cc
@@ -4,9 +4,10 @@
#include <dirent.h>
#include <fcntl.h>
-#include <pthread.h>
#include <sys/mman.h>
+#ifndef __Fuchsia__
#include <sys/resource.h>
+#endif
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
@@ -72,7 +73,14 @@ Status PosixError(const std::string& context, int error_number) {
class Limiter {
public:
// Limit maximum number of resources to |max_acquires|.
- Limiter(int max_acquires) : acquires_allowed_(max_acquires) {}
+ Limiter(int max_acquires)
+ :
+#if !defined(NDEBUG)
+ max_acquires_(max_acquires),
+#endif // !defined(NDEBUG)
+ acquires_allowed_(max_acquires) {
+ assert(max_acquires >= 0);
+ }
Limiter(const Limiter&) = delete;
Limiter operator=(const Limiter&) = delete;
@@ -85,15 +93,35 @@ class Limiter {
if (old_acquires_allowed > 0) return true;
- acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
+ int pre_increment_acquires_allowed =
+ acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
+
+ // Silence compiler warnings about unused arguments when NDEBUG is defined.
+ (void)pre_increment_acquires_allowed;
+ // If the check below fails, Release() was called more times than acquire.
+ assert(pre_increment_acquires_allowed < max_acquires_);
+
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); }
+ void Release() {
+ int old_acquires_allowed =
+ acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
+
+ // Silence compiler warnings about unused arguments when NDEBUG is defined.
+ (void)old_acquires_allowed;
+ // If the check below fails, Release() was called more times than acquire.
+ assert(old_acquires_allowed < max_acquires_);
+ }
private:
+#if !defined(NDEBUG)
+ // Catches an excessive number of Release() calls.
+ const int max_acquires_;
+#endif // !defined(NDEBUG)
+
// The number of available resources.
//
// This is a counter and is not tied to the invariants of any other class, so
@@ -108,7 +136,7 @@ class Limiter {
class PosixSequentialFile final : public SequentialFile {
public:
PosixSequentialFile(std::string filename, int fd)
- : fd_(fd), filename_(filename) {}
+ : fd_(fd), filename_(std::move(filename)) {}
~PosixSequentialFile() override { close(fd_); }
Status Read(size_t n, Slice* result, char* scratch) override {
@@ -214,7 +242,7 @@ class PosixMmapReadableFile final : public RandomAccessFile {
// 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
+ // acquired 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)
@@ -587,7 +615,7 @@ class PosixEnv : public Env {
return Status::OK();
}
- Status DeleteFile(const std::string& filename) override {
+ Status RemoveFile(const std::string& filename) override {
if (::unlink(filename.c_str()) != 0) {
return PosixError(filename, errno);
}
@@ -601,7 +629,7 @@ class PosixEnv : public Env {
return Status::OK();
}
- Status DeleteDir(const std::string& dirname) override {
+ Status RemoveDir(const std::string& dirname) override {
if (::rmdir(dirname.c_str()) != 0) {
return PosixError(dirname, errno);
}
@@ -728,7 +756,7 @@ class PosixEnv : public Env {
// Instances are constructed on the thread calling Schedule() and used on the
// background thread.
//
- // This structure is thread-safe beacuse it is immutable.
+ // This structure is thread-safe because it is immutable.
struct BackgroundWorkItem {
explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
: function(function), arg(arg) {}
@@ -757,6 +785,10 @@ int MaxOpenFiles() {
if (g_open_read_only_file_limit >= 0) {
return g_open_read_only_file_limit;
}
+#ifdef __Fuchsia__
+ // Fuchsia doesn't implement getrlimit.
+ g_open_read_only_file_limit = 50;
+#else
struct ::rlimit rlim;
if (::getrlimit(RLIMIT_NOFILE, &rlim)) {
// getrlimit failed, fallback to hard-coded default.
@@ -767,6 +799,7 @@ int MaxOpenFiles() {
// Allow use of 20% of available file descriptors for read-only files.
g_open_read_only_file_limit = rlim.rlim_cur / 5;
}
+#endif
return g_open_read_only_file_limit;
}
@@ -837,7 +870,7 @@ class SingletonEnv {
public:
SingletonEnv() {
#if !defined(NDEBUG)
- env_initialized_.store(true, std::memory_order::memory_order_relaxed);
+ env_initialized_.store(true, std::memory_order_relaxed);
#endif // !defined(NDEBUG)
static_assert(sizeof(env_storage_) >= sizeof(EnvType),
"env_storage_ will not fit the Env");
@@ -854,7 +887,7 @@ class SingletonEnv {
static void AssertEnvNotInitialized() {
#if !defined(NDEBUG)
- assert(!env_initialized_.load(std::memory_order::memory_order_relaxed));
+ assert(!env_initialized_.load(std::memory_order_relaxed));
#endif // !defined(NDEBUG)
}
diff --git a/util/env_posix_test.cc b/util/env_posix_test.cc
index 9675d73..34bda62 100644
--- a/util/env_posix_test.cc
+++ b/util/env_posix_test.cc
@@ -13,10 +13,11 @@
#include <unordered_set>
#include <vector>
+#include "gtest/gtest.h"
#include "leveldb/env.h"
#include "port/port.h"
#include "util/env_posix_test_helper.h"
-#include "util/testharness.h"
+#include "util/testutil.h"
#if HAVE_O_CLOEXEC
@@ -168,7 +169,7 @@ namespace leveldb {
static const int kReadOnlyFileLimit = 4;
static const int kMMapLimit = 4;
-class EnvPosixTest {
+class EnvPosixTest : public testing::Test {
public:
static void SetFileLimits(int read_only_file_limit, int mmap_limit) {
EnvPosixTestHelper::SetReadOnlyFDLimit(read_only_file_limit);
@@ -180,150 +181,150 @@ class EnvPosixTest {
Env* env_;
};
-TEST(EnvPosixTest, TestOpenOnRead) {
+TEST_F(EnvPosixTest, TestOpenOnRead) {
// Write some test data to a single file that will be opened |n| times.
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string test_file = test_dir + "/open_on_read.txt";
- FILE* f = fopen(test_file.c_str(), "we");
+ FILE* f = std::fopen(test_file.c_str(), "we");
ASSERT_TRUE(f != nullptr);
const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
fputs(kFileData, f);
- fclose(f);
+ std::fclose(f);
// Open test file some number above the sum of the two limits to force
// open-on-read behavior of POSIX Env leveldb::RandomAccessFile.
const int kNumFiles = kReadOnlyFileLimit + kMMapLimit + 5;
leveldb::RandomAccessFile* files[kNumFiles] = {0};
for (int i = 0; i < kNumFiles; i++) {
- ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i]));
+ ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(test_file, &files[i]));
}
char scratch;
Slice read_result;
for (int i = 0; i < kNumFiles; i++) {
- ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch));
+ ASSERT_LEVELDB_OK(files[i]->Read(i, 1, &read_result, &scratch));
ASSERT_EQ(kFileData[i], read_result[0]);
}
for (int i = 0; i < kNumFiles; i++) {
delete files[i];
}
- ASSERT_OK(env_->DeleteFile(test_file));
+ ASSERT_LEVELDB_OK(env_->RemoveFile(test_file));
}
#if HAVE_O_CLOEXEC
-TEST(EnvPosixTest, TestCloseOnExecSequentialFile) {
+TEST_F(EnvPosixTest, TestCloseOnExecSequentialFile) {
std::unordered_set<int> open_fds;
GetOpenFileDescriptors(&open_fds);
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string file_path = test_dir + "/close_on_exec_sequential.txt";
- ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
+ ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
leveldb::SequentialFile* file = nullptr;
- ASSERT_OK(env_->NewSequentialFile(file_path, &file));
+ ASSERT_LEVELDB_OK(env_->NewSequentialFile(file_path, &file));
CheckCloseOnExecDoesNotLeakFDs(open_fds);
delete file;
- ASSERT_OK(env_->DeleteFile(file_path));
+ ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}
-TEST(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
+TEST_F(EnvPosixTest, TestCloseOnExecRandomAccessFile) {
std::unordered_set<int> open_fds;
GetOpenFileDescriptors(&open_fds);
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string file_path = test_dir + "/close_on_exec_random_access.txt";
- ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
+ ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
// Exhaust the RandomAccessFile mmap limit. This way, the test
// RandomAccessFile instance below is backed by a file descriptor, not by an
// mmap region.
- leveldb::RandomAccessFile* mmapped_files[kReadOnlyFileLimit] = {nullptr};
- for (int i = 0; i < kReadOnlyFileLimit; i++) {
- ASSERT_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));
+ leveldb::RandomAccessFile* mmapped_files[kMMapLimit];
+ for (int i = 0; i < kMMapLimit; i++) {
+ ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &mmapped_files[i]));
}
leveldb::RandomAccessFile* file = nullptr;
- ASSERT_OK(env_->NewRandomAccessFile(file_path, &file));
+ ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(file_path, &file));
CheckCloseOnExecDoesNotLeakFDs(open_fds);
delete file;
- for (int i = 0; i < kReadOnlyFileLimit; i++) {
+ for (int i = 0; i < kMMapLimit; i++) {
delete mmapped_files[i];
}
- ASSERT_OK(env_->DeleteFile(file_path));
+ ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}
-TEST(EnvPosixTest, TestCloseOnExecWritableFile) {
+TEST_F(EnvPosixTest, TestCloseOnExecWritableFile) {
std::unordered_set<int> open_fds;
GetOpenFileDescriptors(&open_fds);
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string file_path = test_dir + "/close_on_exec_writable.txt";
- ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
+ ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
leveldb::WritableFile* file = nullptr;
- ASSERT_OK(env_->NewWritableFile(file_path, &file));
+ ASSERT_LEVELDB_OK(env_->NewWritableFile(file_path, &file));
CheckCloseOnExecDoesNotLeakFDs(open_fds);
delete file;
- ASSERT_OK(env_->DeleteFile(file_path));
+ ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}
-TEST(EnvPosixTest, TestCloseOnExecAppendableFile) {
+TEST_F(EnvPosixTest, TestCloseOnExecAppendableFile) {
std::unordered_set<int> open_fds;
GetOpenFileDescriptors(&open_fds);
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string file_path = test_dir + "/close_on_exec_appendable.txt";
- ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
+ ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
leveldb::WritableFile* file = nullptr;
- ASSERT_OK(env_->NewAppendableFile(file_path, &file));
+ ASSERT_LEVELDB_OK(env_->NewAppendableFile(file_path, &file));
CheckCloseOnExecDoesNotLeakFDs(open_fds);
delete file;
- ASSERT_OK(env_->DeleteFile(file_path));
+ ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}
-TEST(EnvPosixTest, TestCloseOnExecLockFile) {
+TEST_F(EnvPosixTest, TestCloseOnExecLockFile) {
std::unordered_set<int> open_fds;
GetOpenFileDescriptors(&open_fds);
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string file_path = test_dir + "/close_on_exec_lock.txt";
- ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
+ ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
leveldb::FileLock* lock = nullptr;
- ASSERT_OK(env_->LockFile(file_path, &lock));
+ ASSERT_LEVELDB_OK(env_->LockFile(file_path, &lock));
CheckCloseOnExecDoesNotLeakFDs(open_fds);
- ASSERT_OK(env_->UnlockFile(lock));
+ ASSERT_LEVELDB_OK(env_->UnlockFile(lock));
- ASSERT_OK(env_->DeleteFile(file_path));
+ ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}
-TEST(EnvPosixTest, TestCloseOnExecLogger) {
+TEST_F(EnvPosixTest, TestCloseOnExecLogger) {
std::unordered_set<int> open_fds;
GetOpenFileDescriptors(&open_fds);
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string file_path = test_dir + "/close_on_exec_logger.txt";
- ASSERT_OK(WriteStringToFile(env_, "0123456789", file_path));
+ ASSERT_LEVELDB_OK(WriteStringToFile(env_, "0123456789", file_path));
leveldb::Logger* file = nullptr;
- ASSERT_OK(env_->NewLogger(file_path, &file));
+ ASSERT_LEVELDB_OK(env_->NewLogger(file_path, &file));
CheckCloseOnExecDoesNotLeakFDs(open_fds);
delete file;
- ASSERT_OK(env_->DeleteFile(file_path));
+ ASSERT_LEVELDB_OK(env_->RemoveFile(file_path));
}
#endif // HAVE_O_CLOEXEC
@@ -346,5 +347,7 @@ int main(int argc, char** argv) {
// All tests currently run with the same read-only file limits.
leveldb::EnvPosixTest::SetFileLimits(leveldb::kReadOnlyFileLimit,
leveldb::kMMapLimit);
- return leveldb::test::RunAllTests();
+
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
}
diff --git a/util/env_test.cc b/util/env_test.cc
index 2023e35..47174f5 100644
--- a/util/env_test.cc
+++ b/util/env_test.cc
@@ -6,30 +6,30 @@
#include <algorithm>
+#include "gtest/gtest.h"
#include "port/port.h"
#include "port/thread_annotations.h"
#include "util/mutexlock.h"
-#include "util/testharness.h"
#include "util/testutil.h"
namespace leveldb {
-class EnvTest {
+class EnvTest : public testing::Test {
public:
EnvTest() : env_(Env::Default()) {}
Env* env_;
};
-TEST(EnvTest, ReadWrite) {
+TEST_F(EnvTest, ReadWrite) {
Random rnd(test::RandomSeed());
// Get file to use for testing.
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string test_file_name = test_dir + "/open_on_read.txt";
WritableFile* writable_file;
- ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file));
+ ASSERT_LEVELDB_OK(env_->NewWritableFile(test_file_name, &writable_file));
// Fill a file with data generated via a sequence of randomly sized writes.
static const size_t kDataSize = 10 * 1048576;
@@ -38,26 +38,26 @@ TEST(EnvTest, ReadWrite) {
int len = rnd.Skewed(18); // Up to 2^18 - 1, but typically much smaller
std::string r;
test::RandomString(&rnd, len, &r);
- ASSERT_OK(writable_file->Append(r));
+ ASSERT_LEVELDB_OK(writable_file->Append(r));
data += r;
if (rnd.OneIn(10)) {
- ASSERT_OK(writable_file->Flush());
+ ASSERT_LEVELDB_OK(writable_file->Flush());
}
}
- ASSERT_OK(writable_file->Sync());
- ASSERT_OK(writable_file->Close());
+ ASSERT_LEVELDB_OK(writable_file->Sync());
+ ASSERT_LEVELDB_OK(writable_file->Close());
delete writable_file;
// Read all data using a sequence of randomly sized reads.
SequentialFile* sequential_file;
- ASSERT_OK(env_->NewSequentialFile(test_file_name, &sequential_file));
+ ASSERT_LEVELDB_OK(env_->NewSequentialFile(test_file_name, &sequential_file));
std::string read_result;
std::string scratch;
while (read_result.size() < data.size()) {
int len = std::min<int>(rnd.Skewed(18), data.size() - read_result.size());
scratch.resize(std::max(len, 1)); // at least 1 so &scratch[0] is legal
Slice read;
- ASSERT_OK(sequential_file->Read(len, &read, &scratch[0]));
+ ASSERT_LEVELDB_OK(sequential_file->Read(len, &read, &scratch[0]));
if (len > 0) {
ASSERT_GT(read.size(), 0);
}
@@ -68,7 +68,7 @@ TEST(EnvTest, ReadWrite) {
delete sequential_file;
}
-TEST(EnvTest, RunImmediately) {
+TEST_F(EnvTest, RunImmediately) {
struct RunState {
port::Mutex mu;
port::CondVar cvar{&mu};
@@ -92,7 +92,7 @@ TEST(EnvTest, RunImmediately) {
}
}
-TEST(EnvTest, RunMany) {
+TEST_F(EnvTest, RunMany) {
struct RunState {
port::Mutex mu;
port::CondVar cvar{&mu};
@@ -151,7 +151,7 @@ static void ThreadBody(void* arg) {
s->mu.Unlock();
}
-TEST(EnvTest, StartThread) {
+TEST_F(EnvTest, StartThread) {
State state(0, 3);
for (int i = 0; i < 3; i++) {
env_->StartThread(&ThreadBody, &state);
@@ -164,10 +164,10 @@ TEST(EnvTest, StartThread) {
ASSERT_EQ(state.val, 3);
}
-TEST(EnvTest, TestOpenNonExistentFile) {
+TEST_F(EnvTest, TestOpenNonExistentFile) {
// Write some test data to a single file that will be opened |n| times.
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string non_existent_file = test_dir + "/non_existent_file";
ASSERT_TRUE(!env_->FileExists(non_existent_file));
@@ -182,54 +182,52 @@ TEST(EnvTest, TestOpenNonExistentFile) {
ASSERT_TRUE(status.IsNotFound());
}
-TEST(EnvTest, ReopenWritableFile) {
+TEST_F(EnvTest, ReopenWritableFile) {
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string test_file_name = test_dir + "/reopen_writable_file.txt";
- env_->DeleteFile(test_file_name);
+ env_->RemoveFile(test_file_name);
WritableFile* writable_file;
- ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file));
+ ASSERT_LEVELDB_OK(env_->NewWritableFile(test_file_name, &writable_file));
std::string data("hello world!");
- ASSERT_OK(writable_file->Append(data));
- ASSERT_OK(writable_file->Close());
+ ASSERT_LEVELDB_OK(writable_file->Append(data));
+ ASSERT_LEVELDB_OK(writable_file->Close());
delete writable_file;
- ASSERT_OK(env_->NewWritableFile(test_file_name, &writable_file));
+ ASSERT_LEVELDB_OK(env_->NewWritableFile(test_file_name, &writable_file));
data = "42";
- ASSERT_OK(writable_file->Append(data));
- ASSERT_OK(writable_file->Close());
+ ASSERT_LEVELDB_OK(writable_file->Append(data));
+ ASSERT_LEVELDB_OK(writable_file->Close());
delete writable_file;
- ASSERT_OK(ReadFileToString(env_, test_file_name, &data));
+ ASSERT_LEVELDB_OK(ReadFileToString(env_, test_file_name, &data));
ASSERT_EQ(std::string("42"), data);
- env_->DeleteFile(test_file_name);
+ env_->RemoveFile(test_file_name);
}
-TEST(EnvTest, ReopenAppendableFile) {
+TEST_F(EnvTest, ReopenAppendableFile) {
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string test_file_name = test_dir + "/reopen_appendable_file.txt";
- env_->DeleteFile(test_file_name);
+ env_->RemoveFile(test_file_name);
WritableFile* appendable_file;
- ASSERT_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
+ ASSERT_LEVELDB_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
std::string data("hello world!");
- ASSERT_OK(appendable_file->Append(data));
- ASSERT_OK(appendable_file->Close());
+ ASSERT_LEVELDB_OK(appendable_file->Append(data));
+ ASSERT_LEVELDB_OK(appendable_file->Close());
delete appendable_file;
- ASSERT_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
+ ASSERT_LEVELDB_OK(env_->NewAppendableFile(test_file_name, &appendable_file));
data = "42";
- ASSERT_OK(appendable_file->Append(data));
- ASSERT_OK(appendable_file->Close());
+ ASSERT_LEVELDB_OK(appendable_file->Append(data));
+ ASSERT_LEVELDB_OK(appendable_file->Close());
delete appendable_file;
- ASSERT_OK(ReadFileToString(env_, test_file_name, &data));
+ ASSERT_LEVELDB_OK(ReadFileToString(env_, test_file_name, &data));
ASSERT_EQ(std::string("hello world!42"), data);
- env_->DeleteFile(test_file_name);
+ env_->RemoveFile(test_file_name);
}
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/env_windows.cc b/util/env_windows.cc
index 2dd7794..1c74b02 100644
--- a/util/env_windows.cc
+++ b/util/env_windows.cc
@@ -33,10 +33,6 @@
#include "util/mutexlock.h"
#include "util/windows_logger.h"
-#if defined(DeleteFile)
-#undef DeleteFile
-#endif // defined(DeleteFile)
-
namespace leveldb {
namespace {
@@ -118,7 +114,14 @@ class ScopedHandle {
class Limiter {
public:
// Limit maximum number of resources to |max_acquires|.
- Limiter(int max_acquires) : acquires_allowed_(max_acquires) {}
+ Limiter(int max_acquires)
+ :
+#if !defined(NDEBUG)
+ max_acquires_(max_acquires),
+#endif // !defined(NDEBUG)
+ acquires_allowed_(max_acquires) {
+ assert(max_acquires >= 0);
+ }
Limiter(const Limiter&) = delete;
Limiter operator=(const Limiter&) = delete;
@@ -137,9 +140,22 @@ class Limiter {
// Release a resource acquired by a previous call to Acquire() that returned
// true.
- void Release() { acquires_allowed_.fetch_add(1, std::memory_order_relaxed); }
+ void Release() {
+ int old_acquires_allowed =
+ acquires_allowed_.fetch_add(1, std::memory_order_relaxed);
+
+ // Silence compiler warnings about unused arguments when NDEBUG is defined.
+ (void)old_acquires_allowed;
+ // If the check below fails, Release() was called more times than acquire.
+ assert(old_acquires_allowed < max_acquires_);
+ }
private:
+#if !defined(NDEBUG)
+ // Catches an excessive number of Release() calls.
+ const int max_acquires_;
+#endif // !defined(NDEBUG)
+
// The number of available resources.
//
// This is a counter and is not tied to the invariants of any other class, so
@@ -505,7 +521,7 @@ class WindowsEnv : public Env {
return Status::OK();
}
- Status DeleteFile(const std::string& filename) override {
+ Status RemoveFile(const std::string& filename) override {
if (!::DeleteFileA(filename.c_str())) {
return WindowsError(filename, ::GetLastError());
}
@@ -519,7 +535,7 @@ class WindowsEnv : public Env {
return Status::OK();
}
- Status DeleteDir(const std::string& dirname) override {
+ Status RemoveDir(const std::string& dirname) override {
if (!::RemoveDirectoryA(dirname.c_str())) {
return WindowsError(dirname, ::GetLastError());
}
@@ -626,7 +642,7 @@ class WindowsEnv : public Env {
}
Status NewLogger(const std::string& filename, Logger** result) override {
- std::FILE* fp = std::fopen(filename.c_str(), "w");
+ std::FILE* fp = std::fopen(filename.c_str(), "wN");
if (fp == nullptr) {
*result = nullptr;
return WindowsError(filename, ::GetLastError());
@@ -665,7 +681,7 @@ class WindowsEnv : public Env {
// Instances are constructed on the thread calling Schedule() and used on the
// background thread.
//
- // This structure is thread-safe beacuse it is immutable.
+ // This structure is thread-safe because it is immutable.
struct BackgroundWorkItem {
explicit BackgroundWorkItem(void (*function)(void* arg), void* arg)
: function(function), arg(arg) {}
@@ -749,7 +765,7 @@ class SingletonEnv {
public:
SingletonEnv() {
#if !defined(NDEBUG)
- env_initialized_.store(true, std::memory_order::memory_order_relaxed);
+ env_initialized_.store(true, std::memory_order_relaxed);
#endif // !defined(NDEBUG)
static_assert(sizeof(env_storage_) >= sizeof(EnvType),
"env_storage_ will not fit the Env");
@@ -766,7 +782,7 @@ class SingletonEnv {
static void AssertEnvNotInitialized() {
#if !defined(NDEBUG)
- assert(!env_initialized_.load(std::memory_order::memory_order_relaxed));
+ assert(!env_initialized_.load(std::memory_order_relaxed));
#endif // !defined(NDEBUG)
}
diff --git a/util/env_windows_test.cc b/util/env_windows_test.cc
index 3c22133..d6822d2 100644
--- a/util/env_windows_test.cc
+++ b/util/env_windows_test.cc
@@ -2,17 +2,17 @@
// 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 "gtest/gtest.h"
#include "leveldb/env.h"
-
#include "port/port.h"
#include "util/env_windows_test_helper.h"
-#include "util/testharness.h"
+#include "util/testutil.h"
namespace leveldb {
static const int kMMapLimit = 4;
-class EnvWindowsTest {
+class EnvWindowsTest : public testing::Test {
public:
static void SetFileLimits(int mmap_limit) {
EnvWindowsTestHelper::SetReadOnlyMMapLimit(mmap_limit);
@@ -23,17 +23,17 @@ class EnvWindowsTest {
Env* env_;
};
-TEST(EnvWindowsTest, TestOpenOnRead) {
+TEST_F(EnvWindowsTest, TestOpenOnRead) {
// Write some test data to a single file that will be opened |n| times.
std::string test_dir;
- ASSERT_OK(env_->GetTestDirectory(&test_dir));
+ ASSERT_LEVELDB_OK(env_->GetTestDirectory(&test_dir));
std::string test_file = test_dir + "/open_on_read.txt";
- FILE* f = fopen(test_file.c_str(), "w");
+ FILE* f = std::fopen(test_file.c_str(), "w");
ASSERT_TRUE(f != nullptr);
const char kFileData[] = "abcdefghijklmnopqrstuvwxyz";
fputs(kFileData, f);
- fclose(f);
+ std::fclose(f);
// Open test file some number above the sum of the two limits to force
// leveldb::WindowsEnv to switch from mapping the file into memory
@@ -41,18 +41,18 @@ TEST(EnvWindowsTest, TestOpenOnRead) {
const int kNumFiles = kMMapLimit + 5;
leveldb::RandomAccessFile* files[kNumFiles] = {0};
for (int i = 0; i < kNumFiles; i++) {
- ASSERT_OK(env_->NewRandomAccessFile(test_file, &files[i]));
+ ASSERT_LEVELDB_OK(env_->NewRandomAccessFile(test_file, &files[i]));
}
char scratch;
Slice read_result;
for (int i = 0; i < kNumFiles; i++) {
- ASSERT_OK(files[i]->Read(i, 1, &read_result, &scratch));
+ ASSERT_LEVELDB_OK(files[i]->Read(i, 1, &read_result, &scratch));
ASSERT_EQ(kFileData[i], read_result[0]);
}
for (int i = 0; i < kNumFiles; i++) {
delete files[i];
}
- ASSERT_OK(env_->DeleteFile(test_file));
+ ASSERT_LEVELDB_OK(env_->RemoveFile(test_file));
}
} // namespace leveldb
@@ -60,5 +60,6 @@ TEST(EnvWindowsTest, TestOpenOnRead) {
int main(int argc, char** argv) {
// All tests currently run with the same read-only file limits.
leveldb::EnvWindowsTest::SetFileLimits(leveldb::kMMapLimit);
- return leveldb::test::RunAllTests();
+ testing::InitGoogleTest(&argc, argv);
+ return RUN_ALL_TESTS();
}
diff --git a/util/hash.cc b/util/hash.cc
index dd47c11..8122fa8 100644
--- a/util/hash.cc
+++ b/util/hash.cc
@@ -4,7 +4,7 @@
#include "util/hash.h"
-#include <string.h>
+#include <cstring>
#include "util/coding.h"
diff --git a/util/hash.h b/util/hash.h
index 74bdb6e..87ab279 100644
--- a/util/hash.h
+++ b/util/hash.h
@@ -7,8 +7,8 @@
#ifndef STORAGE_LEVELDB_UTIL_HASH_H_
#define STORAGE_LEVELDB_UTIL_HASH_H_
-#include <stddef.h>
-#include <stdint.h>
+#include <cstddef>
+#include <cstdint>
namespace leveldb {
diff --git a/util/hash_test.cc b/util/hash_test.cc
index 21f8171..0ea5977 100644
--- a/util/hash_test.cc
+++ b/util/hash_test.cc
@@ -3,11 +3,10 @@
// found in the LICENSE file. See the AUTHORS file for names of contributors.
#include "util/hash.h"
-#include "util/testharness.h"
-namespace leveldb {
+#include "gtest/gtest.h"
-class HASH {};
+namespace leveldb {
TEST(HASH, SignedUnsignedIssue) {
const uint8_t data1[1] = {0x62};
@@ -40,5 +39,3 @@ TEST(HASH, SignedUnsignedIssue) {
}
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/histogram.cc b/util/histogram.cc
index 65092c8..7af4030 100644
--- a/util/histogram.cc
+++ b/util/histogram.cc
@@ -4,8 +4,8 @@
#include "util/histogram.h"
-#include <math.h>
-#include <stdio.h>
+#include <cmath>
+#include <cstdio>
#include "port/port.h"
@@ -241,11 +241,11 @@ double Histogram::StandardDeviation() const {
std::string Histogram::ToString() const {
std::string r;
char buf[200];
- snprintf(buf, sizeof(buf), "Count: %.0f Average: %.4f StdDev: %.2f\n", num_,
- Average(), StandardDeviation());
+ std::snprintf(buf, sizeof(buf), "Count: %.0f Average: %.4f StdDev: %.2f\n",
+ num_, Average(), StandardDeviation());
r.append(buf);
- snprintf(buf, sizeof(buf), "Min: %.4f Median: %.4f Max: %.4f\n",
- (num_ == 0.0 ? 0.0 : min_), Median(), max_);
+ 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_;
@@ -253,12 +253,12 @@ std::string Histogram::ToString() const {
for (int b = 0; b < kNumBuckets; b++) {
if (buckets_[b] <= 0.0) continue;
sum += buckets_[b];
- snprintf(buf, sizeof(buf), "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ",
- ((b == 0) ? 0.0 : kBucketLimit[b - 1]), // left
- kBucketLimit[b], // right
- buckets_[b], // count
- mult * buckets_[b], // percentage
- mult * sum); // cumulative percentage
+ 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%.
diff --git a/util/logging.cc b/util/logging.cc
index 75e9d03..8d6fb5b 100644
--- a/util/logging.cc
+++ b/util/logging.cc
@@ -4,11 +4,9 @@
#include "util/logging.h"
-#include <errno.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-
+#include <cstdarg>
+#include <cstdio>
+#include <cstdlib>
#include <limits>
#include "leveldb/env.h"
@@ -18,7 +16,7 @@ namespace leveldb {
void AppendNumberTo(std::string* str, uint64_t num) {
char buf[30];
- snprintf(buf, sizeof(buf), "%llu", (unsigned long long)num);
+ std::snprintf(buf, sizeof(buf), "%llu", static_cast<unsigned long long>(num));
str->append(buf);
}
@@ -29,8 +27,8 @@ void AppendEscapedStringTo(std::string* str, const Slice& value) {
str->push_back(c);
} else {
char buf[10];
- snprintf(buf, sizeof(buf), "\\x%02x",
- static_cast<unsigned int>(c) & 0xff);
+ std::snprintf(buf, sizeof(buf), "\\x%02x",
+ static_cast<unsigned int>(c) & 0xff);
str->append(buf);
}
}
diff --git a/util/logging.h b/util/logging.h
index 8ff2da8..a0394b2 100644
--- a/util/logging.h
+++ b/util/logging.h
@@ -8,9 +8,8 @@
#ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_
#define STORAGE_LEVELDB_UTIL_LOGGING_H_
-#include <stdint.h>
-#include <stdio.h>
-
+#include <cstdint>
+#include <cstdio>
#include <string>
#include "port/port.h"
diff --git a/util/logging_test.cc b/util/logging_test.cc
index 389cbeb..1746c57 100644
--- a/util/logging_test.cc
+++ b/util/logging_test.cc
@@ -2,17 +2,16 @@
// 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 <limits>
#include <string>
+#include "gtest/gtest.h"
#include "leveldb/slice.h"
-#include "util/logging.h"
-#include "util/testharness.h"
namespace leveldb {
-class Logging {};
-
TEST(Logging, NumberToString) {
ASSERT_EQ("0", NumberToString(0));
ASSERT_EQ("1", NumberToString(1));
@@ -139,5 +138,3 @@ TEST(Logging, ConsumeDecimalNumberNoDigits) {
}
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/no_destructor_test.cc b/util/no_destructor_test.cc
index b41caca..e3602cc 100644
--- a/util/no_destructor_test.cc
+++ b/util/no_destructor_test.cc
@@ -2,12 +2,13 @@
// 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/no_destructor.h"
+
#include <cstdint>
#include <cstdlib>
#include <utility>
-#include "util/no_destructor.h"
-#include "util/testharness.h"
+#include "gtest/gtest.h"
namespace leveldb {
@@ -28,8 +29,6 @@ constexpr const uint64_t kGoldenB = 0xaabbccddeeffaabb;
} // namespace
-class NoDestructorTest {};
-
TEST(NoDestructorTest, StackInstance) {
NoDestructor<DoNotDestruct> instance(kGoldenA, kGoldenB);
ASSERT_EQ(kGoldenA, instance.get()->a);
@@ -43,5 +42,3 @@ TEST(NoDestructorTest, StaticInstance) {
}
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/posix_logger.h b/util/posix_logger.h
index 28e15d1..6bbc1a0 100644
--- a/util/posix_logger.h
+++ b/util/posix_logger.h
@@ -30,7 +30,7 @@ class PosixLogger final : public Logger {
~PosixLogger() override { std::fclose(fp_); }
- void Logv(const char* format, va_list arguments) override {
+ 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);
@@ -62,7 +62,7 @@ class PosixLogger final : public Logger {
(iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
// Print the header into the buffer.
- int buffer_offset = snprintf(
+ 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,
@@ -98,8 +98,8 @@ class PosixLogger final : public Logger {
}
// The dynamically-allocated buffer was incorrectly sized. This should
- // not happen, assuming a correct implementation of (v)snprintf. Fail
- // in tests, recover by truncating the log message in production.
+ // 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;
}
diff --git a/util/random.h b/util/random.h
index 76f7daf..fe76ab4 100644
--- a/util/random.h
+++ b/util/random.h
@@ -5,7 +5,7 @@
#ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_
#define STORAGE_LEVELDB_UTIL_RANDOM_H_
-#include <stdint.h>
+#include <cstdint>
namespace leveldb {
diff --git a/util/status.cc b/util/status.cc
index 15ce747..0559f5b 100644
--- a/util/status.cc
+++ b/util/status.cc
@@ -4,7 +4,7 @@
#include "leveldb/status.h"
-#include <stdio.h>
+#include <cstdio>
#include "port/port.h"
@@ -12,9 +12,9 @@ namespace leveldb {
const char* Status::CopyState(const char* state) {
uint32_t size;
- memcpy(&size, state, sizeof(size));
+ std::memcpy(&size, state, sizeof(size));
char* result = new char[size + 5];
- memcpy(result, state, size + 5);
+ std::memcpy(result, state, size + 5);
return result;
}
@@ -24,13 +24,13 @@ Status::Status(Code code, const Slice& msg, const Slice& msg2) {
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];
- memcpy(result, &size, sizeof(size));
+ std::memcpy(result, &size, sizeof(size));
result[4] = static_cast<char>(code);
- memcpy(result + 5, msg.data(), len1);
+ std::memcpy(result + 5, msg.data(), len1);
if (len2) {
result[5 + len1] = ':';
result[6 + len1] = ' ';
- memcpy(result + 7 + len1, msg2.data(), len2);
+ std::memcpy(result + 7 + len1, msg2.data(), len2);
}
state_ = result;
}
@@ -61,14 +61,14 @@ std::string Status::ToString() const {
type = "IO error: ";
break;
default:
- snprintf(tmp, sizeof(tmp),
- "Unknown code(%d): ", static_cast<int>(code()));
+ std::snprintf(tmp, sizeof(tmp),
+ "Unknown code(%d): ", static_cast<int>(code()));
type = tmp;
break;
}
std::string result(type);
uint32_t length;
- memcpy(&length, state_, sizeof(length));
+ std::memcpy(&length, state_, sizeof(length));
result.append(state_ + 5, length);
return result;
}
diff --git a/util/status_test.cc b/util/status_test.cc
index 2842319..dbf5faa 100644
--- a/util/status_test.cc
+++ b/util/status_test.cc
@@ -2,11 +2,12 @@
// 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 <utility>
+#include "gtest/gtest.h"
#include "leveldb/slice.h"
-#include "leveldb/status.h"
-#include "util/testharness.h"
namespace leveldb {
@@ -36,5 +37,3 @@ TEST(Status, MoveConstructor) {
}
} // namespace leveldb
-
-int main(int argc, char** argv) { return leveldb::test::RunAllTests(); }
diff --git a/util/testharness.cc b/util/testharness.cc
deleted file mode 100644
index 318ecfa..0000000
--- a/util/testharness.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file. See the AUTHORS file for names of contributors.
-
-#include "util/testharness.h"
-
-#include <stdlib.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-
-#include <string>
-#include <vector>
-
-#include "leveldb/env.h"
-
-namespace leveldb {
-namespace test {
-
-namespace {
-struct Test {
- const char* base;
- const char* name;
- void (*func)();
-};
-std::vector<Test>* tests;
-} // namespace
-
-bool RegisterTest(const char* base, const char* name, void (*func)()) {
- if (tests == nullptr) {
- tests = new std::vector<Test>;
- }
- Test t;
- t.base = base;
- t.name = name;
- t.func = func;
- tests->push_back(t);
- return true;
-}
-
-int RunAllTests() {
- const char* matcher = getenv("LEVELDB_TESTS");
-
- int num = 0;
- if (tests != nullptr) {
- for (size_t i = 0; i < tests->size(); i++) {
- const Test& t = (*tests)[i];
- if (matcher != nullptr) {
- std::string name = t.base;
- name.push_back('.');
- name.append(t.name);
- if (strstr(name.c_str(), matcher) == nullptr) {
- continue;
- }
- }
- fprintf(stderr, "==== Test %s.%s\n", t.base, t.name);
- (*t.func)();
- ++num;
- }
- }
- fprintf(stderr, "==== PASSED %d tests\n", num);
- return 0;
-}
-
-std::string TmpDir() {
- std::string dir;
- Status s = Env::Default()->GetTestDirectory(&dir);
- ASSERT_TRUE(s.ok()) << s.ToString();
- return dir;
-}
-
-int RandomSeed() {
- const char* env = getenv("TEST_RANDOM_SEED");
- int result = (env != nullptr ? atoi(env) : 301);
- if (result <= 0) {
- result = 301;
- }
- return result;
-}
-
-} // namespace test
-} // namespace leveldb
diff --git a/util/testharness.h b/util/testharness.h
deleted file mode 100644
index 72cd162..0000000
--- a/util/testharness.h
+++ /dev/null
@@ -1,141 +0,0 @@
-// Copyright (c) 2011 The LevelDB Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style license that can be
-// found in the LICENSE file. See the AUTHORS file for names of contributors.
-
-#ifndef STORAGE_LEVELDB_UTIL_TESTHARNESS_H_
-#define STORAGE_LEVELDB_UTIL_TESTHARNESS_H_
-
-#include <stdio.h>
-#include <stdlib.h>
-
-#include <sstream>
-
-#include "leveldb/status.h"
-
-namespace leveldb {
-namespace test {
-
-// Run some of the tests registered by the TEST() macro. If the
-// environment variable "LEVELDB_TESTS" is not set, runs all tests.
-// Otherwise, runs only the tests whose name contains the value of
-// "LEVELDB_TESTS" as a substring. E.g., suppose the tests are:
-// TEST(Foo, Hello) { ... }
-// TEST(Foo, World) { ... }
-// LEVELDB_TESTS=Hello will run the first test
-// LEVELDB_TESTS=o will run both tests
-// LEVELDB_TESTS=Junk will run no tests
-//
-// Returns 0 if all tests pass.
-// Dies or returns a non-zero value if some test fails.
-int RunAllTests();
-
-// Return the directory to use for temporary storage.
-std::string TmpDir();
-
-// Return a randomization seed for this run. Typically returns the
-// same number on repeated invocations of this binary, but automated
-// runs may be able to vary the seed.
-int RandomSeed();
-
-// An instance of Tester is allocated to hold temporary state during
-// the execution of an assertion.
-class Tester {
- private:
- bool ok_;
- const char* fname_;
- int line_;
- std::stringstream ss_;
-
- public:
- Tester(const char* f, int l) : ok_(true), fname_(f), line_(l) {}
-
- ~Tester() {
- if (!ok_) {
- fprintf(stderr, "%s:%d:%s\n", fname_, line_, ss_.str().c_str());
- exit(1);
- }
- }
-
- Tester& Is(bool b, const char* msg) {
- if (!b) {
- ss_ << " Assertion failure " << msg;
- ok_ = false;
- }
- return *this;
- }
-
- Tester& IsOk(const Status& s) {
- if (!s.ok()) {
- ss_ << " " << s.ToString();
- ok_ = false;
- }
- return *this;
- }
-
-#define BINARY_OP(name, op) \
- template <class X, class Y> \
- Tester& name(const X& x, const Y& y) { \
- if (!(x op y)) { \
- ss_ << " failed: " << x << (" " #op " ") << y; \
- ok_ = false; \
- } \
- return *this; \
- }
-
- BINARY_OP(IsEq, ==)
- BINARY_OP(IsNe, !=)
- BINARY_OP(IsGe, >=)
- BINARY_OP(IsGt, >)
- BINARY_OP(IsLe, <=)
- BINARY_OP(IsLt, <)
-#undef BINARY_OP
-
- // Attach the specified value to the error message if an error has occurred
- template <class V>
- Tester& operator<<(const V& value) {
- if (!ok_) {
- ss_ << " " << value;
- }
- return *this;
- }
-};
-
-#define ASSERT_TRUE(c) ::leveldb::test::Tester(__FILE__, __LINE__).Is((c), #c)
-#define ASSERT_OK(s) ::leveldb::test::Tester(__FILE__, __LINE__).IsOk((s))
-#define ASSERT_EQ(a, b) \
- ::leveldb::test::Tester(__FILE__, __LINE__).IsEq((a), (b))
-#define ASSERT_NE(a, b) \
- ::leveldb::test::Tester(__FILE__, __LINE__).IsNe((a), (b))
-#define ASSERT_GE(a, b) \
- ::leveldb::test::Tester(__FILE__, __LINE__).IsGe((a), (b))
-#define ASSERT_GT(a, b) \
- ::leveldb::test::Tester(__FILE__, __LINE__).IsGt((a), (b))
-#define ASSERT_LE(a, b) \
- ::leveldb::test::Tester(__FILE__, __LINE__).IsLe((a), (b))
-#define ASSERT_LT(a, b) \
- ::leveldb::test::Tester(__FILE__, __LINE__).IsLt((a), (b))
-
-#define TCONCAT(a, b) TCONCAT1(a, b)
-#define TCONCAT1(a, b) a##b
-
-#define TEST(base, name) \
- class TCONCAT(_Test_, name) : public base { \
- public: \
- void _Run(); \
- static void _RunIt() { \
- TCONCAT(_Test_, name) t; \
- t._Run(); \
- } \
- }; \
- bool TCONCAT(_Test_ignored_, name) = ::leveldb::test::RegisterTest( \
- #base, #name, &TCONCAT(_Test_, name)::_RunIt); \
- void TCONCAT(_Test_, name)::_Run()
-
-// Register the specified test. Typically not used directly, but
-// invoked via the macro expansion of TEST.
-bool RegisterTest(const char* base, const char* name, void (*func)());
-
-} // namespace test
-} // namespace leveldb
-
-#endif // STORAGE_LEVELDB_UTIL_TESTHARNESS_H_
diff --git a/util/testutil.cc b/util/testutil.cc
index 6b151b9..5f77b08 100644
--- a/util/testutil.cc
+++ b/util/testutil.cc
@@ -4,6 +4,8 @@
#include "util/testutil.h"
+#include <string>
+
#include "util/random.h"
namespace leveldb {
diff --git a/util/testutil.h b/util/testutil.h
index bb4051b..e0e2d64 100644
--- a/util/testutil.h
+++ b/util/testutil.h
@@ -5,6 +5,8 @@
#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"
@@ -13,6 +15,20 @@
namespace leveldb {
namespace test {
+MATCHER(IsOK, "") { return arg.ok(); }
+
+// Macros for testing the results of functions that return leveldb::Status or
+// absl::StatusOr<T> (for any type T).
+#define EXPECT_LEVELDB_OK(expression) \
+ EXPECT_THAT(expression, leveldb::test::IsOK())
+#define ASSERT_LEVELDB_OK(expression) \
+ ASSERT_THAT(expression, leveldb::test::IsOK())
+
+// Returns the random seed used at the start of the current test run.
+inline int RandomSeed() {
+ return testing::UnitTest::GetInstance()->random_seed();
+}
+
// Store in *dst a random string of length "len" and return a Slice that
// references the generated data.
Slice RandomString(Random* rnd, int len, std::string* dst);
diff --git a/util/windows_logger.h b/util/windows_logger.h
index 9296063..26e6c7b 100644
--- a/util/windows_logger.h
+++ b/util/windows_logger.h
@@ -27,7 +27,7 @@ class WindowsLogger final : public Logger {
~WindowsLogger() override { std::fclose(fp_); }
- void Logv(const char* format, va_list arguments) override {
+ 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);
@@ -56,7 +56,7 @@ class WindowsLogger final : public Logger {
(iteration == 0) ? stack_buffer : new char[dynamic_buffer_size];
// Print the header into the buffer.
- int buffer_offset = snprintf(
+ 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,
@@ -92,8 +92,8 @@ class WindowsLogger final : public Logger {
}
// The dynamically-allocated buffer was incorrectly sized. This should
- // not happen, assuming a correct implementation of (v)snprintf. Fail
- // in tests, recover by truncating the log message in production.
+ // 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;
}