Skip to content

Commit

Permalink
Added native support for Windows.
Browse files Browse the repository at this point in the history
This change adds a native Windows port (port_windows.h) and a
Windows Env (WindowsEnv).

Note1: "small" is defined when including <Windows.h> so some
parameters were renamed to avoid conflict.

Note2: leveldb::Env defines the method: "DeleteFile" which is
also a constant defined when including <Windows.h>. The solution
was to ensure this macro is defined in env.h which forces
the function, when compiled, to be either DeleteFileA or
DeleteFileW when building for MBCS or UNICODE respectively.

This fixes issue google#519.
  • Loading branch information
cmumford committed Aug 31, 2018
1 parent 16a2b8b commit a5888f6
Show file tree
Hide file tree
Showing 12 changed files with 1,116 additions and 15 deletions.
35 changes: 29 additions & 6 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_EXTENSIONS OFF)

# For parallel builds in Visual Studio.
# set(CMAKE_CXX_FLAGS /MP)
add_compile_options($<$<CXX_COMPILER_ID:MSVC>:/MP>)

if (WIN32)
set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_WINDOWS)
# TODO(cmumford): Make UNICODE configurable for Windows.
add_definitions(-D_UNICODE -DUNICODE)
else (WIN32)
set(LEVELDB_PLATFORM_NAME LEVELDB_PLATFORM_POSIX)
endif (WIN32)

option(LEVELDB_BUILD_TESTS "Build LevelDB's unit tests" ON)
option(LEVELDB_BUILD_BENCHMARKS "Build LevelDB's benchmarks" ON)
option(LEVELDB_INSTALL "Install LevelDB's header and library" ON)
Expand Down Expand Up @@ -172,12 +184,19 @@ target_sources(leveldb
"${LEVELDB_PUBLIC_INCLUDE_DIR}/write_batch.h"
)

# POSIX code is specified separately so we can leave it out in the future.
if (WIN32)
target_sources(leveldb
PRIVATE
"${PROJECT_SOURCE_DIR}/util/env_windows.cc"
"${PROJECT_SOURCE_DIR}/util/windows_logger.h"
)
else (WIN32)
target_sources(leveldb
PRIVATE
"${PROJECT_SOURCE_DIR}/util/env_posix.cc"
"${PROJECT_SOURCE_DIR}/util/posix_logger.h"
)
endif (WIN32)

# MemEnv is not part of the interface and could be pulled to a separate library.
target_sources(leveldb
Expand All @@ -196,7 +215,7 @@ target_compile_definitions(leveldb
# Used by include/export.h when building shared libraries.
LEVELDB_COMPILE_LIBRARY
# Used by port/port.h.
LEVELDB_PLATFORM_POSIX=1
${LEVELDB_PLATFORM_NAME}=1
)
if (NOT HAVE_CXX17_HAS_INCLUDE)
target_compile_definitions(leveldb
Expand Down Expand Up @@ -258,7 +277,7 @@ if(LEVELDB_BUILD_TESTS)
target_link_libraries("${test_target_name}" leveldb)
target_compile_definitions("${test_target_name}"
PRIVATE
LEVELDB_PLATFORM_POSIX=1
${LEVELDB_PLATFORM_NAME}=1
)
if (NOT HAVE_CXX17_HAS_INCLUDE)
target_compile_definitions("${test_target_name}"
Expand Down Expand Up @@ -306,8 +325,12 @@ if(LEVELDB_BUILD_TESTS)
leveldb_test("${PROJECT_SOURCE_DIR}/util/logging_test.cc")

# TODO(costan): This test also uses
# "${PROJECT_SOURCE_DIR}/util/env_posix_test_helper.h"
leveldb_test("${PROJECT_SOURCE_DIR}/util/env_posix_test.cc")
# "${PROJECT_SOURCE_DIR}/util/env_{posix|windows}_test_helper.h"
if (WIN32)
leveldb_test("${PROJECT_SOURCE_DIR}/util/env_windows_test.cc")
else (WIN32)
leveldb_test("${PROJECT_SOURCE_DIR}/util/env_posix_test.cc")
endif (WIN32)
endif(NOT BUILD_SHARED_LIBS)
endif(LEVELDB_BUILD_TESTS)

Expand All @@ -331,7 +354,7 @@ if(LEVELDB_BUILD_BENCHMARKS)
target_link_libraries("${bench_target_name}" leveldb)
target_compile_definitions("${bench_target_name}"
PRIVATE
LEVELDB_PLATFORM_POSIX=1
${LEVELDB_PLATFORM_NAME}=1
)
if (NOT HAVE_CXX17_HAS_INCLUDE)
target_compile_definitions("${bench_target_name}"
Expand Down
31 changes: 28 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,36 @@ This project supports [CMake](https://cmake.org/) out of the box.
Quick start:

```bash
mkdir -p build && cd build
cmake -DCMAKE_BUILD_TYPE=Release .. && cmake --build .
mkdir -p build
cd build
cmake -DCMAKE_BUILD_TYPE=Release ..
cmake --build .
```

Please see the CMake documentation and `CMakeLists.txt` for more advanced usage.
Windows:

For Visual Studio 2017:

First generate the Visual Studio project/solution files:

```bash
mkdir -p build
cd build
cmake -G "Visual Studio 15" ..
```
The default default will build for x86. For 64-bit run:

```bash
cmake -G "Visual Studio 15 Win64" ..
```

To compile the Windows solution from the command-line:

```bash
devenv /build Debug leveldb.sln
```

or open leveldb.sln in Visual Studio and build from within.

# Contributing to the leveldb Project

Expand Down
6 changes: 3 additions & 3 deletions db/db_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -471,9 +471,9 @@ class DBTest {

// Do n memtable compactions, each of which produces an sstable
// covering the range [small,large].
void MakeTables(int n, const std::string& small, const std::string& large) {
void MakeTables(int n, const std::string& small_key, const std::string& large) {
for (int i = 0; i < n; i++) {
Put(small, "begin");
Put(small_key, "begin");
Put(large, "end");
dbfull()->TEST_CompactMemTable();
}
Expand Down Expand Up @@ -1655,7 +1655,7 @@ TEST(DBTest, DestroyEmptyDir) {
ASSERT_TRUE(env.FileExists(dbname));
std::vector<std::string> children;
ASSERT_OK(env.GetChildren(dbname, &children));
// The POSIX env does not filter out '.' and '..' special files.
// The stock Env's do not filter out '.' and '..' special files.
ASSERT_EQ(2, children.size());
ASSERT_OK(DestroyDB(dbname, opts));
ASSERT_TRUE(!env.FileExists(dbname));
Expand Down
1 change: 1 addition & 0 deletions db/recovery_test.cc
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ class RecoveryTest {
}

size_t DeleteLogFiles() {
Close();
std::vector<uint64_t> logs = GetFiles(kLogFile);
for (size_t i = 0; i < logs.size(); i++) {
ASSERT_OK(env_->DeleteFile(LogName(logs[i]))) << LogName(logs[i]);
Expand Down
6 changes: 3 additions & 3 deletions db/version_set.cc
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ bool SomeFileOverlapsRange(
uint32_t index = 0;
if (smallest_user_key != nullptr) {
// Find the earliest possible internal key for smallest_user_key
InternalKey small(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek);
index = FindFile(icmp, files, small.Encode());
InternalKey small_key(*smallest_user_key, kMaxSequenceNumber,kValueTypeForSeek);
index = FindFile(icmp, files, small_key.Encode());
}

if (index >= files.size()) {
Expand Down Expand Up @@ -700,7 +700,7 @@ class VersionSet::Builder {
// same as the compaction of 40KB of data. We are a little
// conservative and allow approximately one seek for every 16KB
// of data before triggering a compaction.
f->allowed_seeks = (f->file_size / 16384);
f->allowed_seeks = static_cast<int>((f->file_size / 16384LU));
if (f->allowed_seeks < 100) f->allowed_seeks = 100;

levels_[level].deleted_files.erase(f->number);
Expand Down
12 changes: 12 additions & 0 deletions include/leveldb/env.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,18 @@
#include "leveldb/export.h"
#include "leveldb/status.h"

#if defined(LEVELDB_PLATFORM_WINDOWS)
// leveldb has a method named "DeleteFile" which conflicts with
// the macro in windows.h.
#if !defined(DeleteFile)
#ifdef UNICODE
#define DeleteFile DeleteFileW
#else
#define DeleteFile DeleteFileA
#endif // !UNICODE
#endif // !defined(DeleteFile)
#endif // defined(LEVELDB_PLATFORM_WINDOWS)

namespace leveldb {

class FileLock;
Expand Down
2 changes: 2 additions & 0 deletions port/port.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
// of what the new port_<platform>.h file must provide.
#if defined(LEVELDB_PLATFORM_POSIX)
# include "port/port_stdcxx.h"
#elif defined(LEVELDB_PLATFORM_WINDOWS)
# include "port/port_windows.h"
#elif defined(LEVELDB_PLATFORM_CHROMIUM)
# include "port/port_chromium.h"
#endif
Expand Down
152 changes: 152 additions & 0 deletions port/port_windows.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// Copyright (c) 2018 The LevelDB Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. See the AUTHORS file for names of contributors.

#ifndef STORAGE_LEVELDB_PORT_PORT_WINDOWS_H_
#define STORAGE_LEVELDB_PORT_PORT_WINDOWS_H_

// port/port_config.h availability is automatically detected via __has_include
// in newer compilers. If LEVELDB_HAS_PORT_CONFIG_H is defined, it overrides the
// configuration detection.
#if defined(LEVELDB_HAS_PORT_CONFIG_H)

#if LEVELDB_HAS_PORT_CONFIG_H
#include "port/port_config.h"
#endif // LEVELDB_HAS_PORT_CONFIG_H

#elif defined(__has_include)

#if __has_include("port/port_config.h")
#include "port/port_config.h"
#endif // __has_include("port/port_config.h")

#endif // defined(LEVELDB_HAS_PORT_CONFIG_H)

#if HAVE_CRC32C
#include <crc32c/crc32c.h>
#endif // HAVE_CRC32C
#if HAVE_SNAPPY
#include <snappy.h>
#endif // HAVE_SNAPPY

// Prevent Windows headers from defining min/max macros and instead
// use STL.
#define NOMINMAX

#include <windows.h>
#include <cassert>
#include <condition_variable> // NOLINT
#include <mutex> // NOLINT
#include <string>
#include "port/atomic_pointer.h"
#include "port/thread_annotations.h"

// ssize_t is a POSIX type and not a C++ type so define it.
#if defined(_WIN64)
typedef __int64 ssize_t;
#else
typedef long ssize_t;
#endif

namespace leveldb {
namespace port {

static const bool kLittleEndian = !LEVELDB_IS_BIG_ENDIAN;

class CondVar;

// Thinly wraps std::mutex.
class LOCKABLE Mutex {
public:
Mutex() = default;
~Mutex() = default;

Mutex(const Mutex&) = delete;
Mutex& operator=(const Mutex&) = delete;

void Lock() EXCLUSIVE_LOCK_FUNCTION() { mu_.lock(); }
void Unlock() UNLOCK_FUNCTION() { mu_.unlock(); }
void AssertHeld() ASSERT_EXCLUSIVE_LOCK() {}

private:
friend class CondVar;
std::mutex mu_;
};

// Thinly wraps std::condition_variable.
class CondVar {
public:
explicit CondVar(Mutex* mu) : mu_(mu) { assert(mu != nullptr); }
~CondVar() = default;

CondVar(const CondVar&) = delete;
CondVar& operator=(const CondVar&) = delete;

void Wait() {
std::unique_lock<std::mutex> lock(mu_->mu_, std::adopt_lock);
cv_.wait(lock);
lock.release();
}
void Signal() { cv_.notify_one(); }
void SignalAll() { cv_.notify_all(); }

private:
std::condition_variable cv_;
Mutex* const mu_;
};

using OnceType = std::once_flag;
#define LEVELDB_ONCE_INIT \
{}

// Thinly wraps std::call_once.
inline void InitOnce(OnceType* once, void (*initializer)()) {
std::call_once(*once, *initializer);
}

inline bool Snappy_Compress(const char* input, size_t length,
::std::string* output) {
#if HAVE_SNAPPY
output->resize(snappy::MaxCompressedLength(length));
size_t outlen;
snappy::RawCompress(input, length, &(*output)[0], &outlen);
output->resize(outlen);
return true;
#endif // HAVE_SNAPPY

return false;
}

inline bool Snappy_GetUncompressedLength(const char* input, size_t length,
size_t* result) {
#if HAVE_SNAPPY
return snappy::GetUncompressedLength(input, length, result);
#else
return false;
#endif // HAVE_SNAPPY
}

inline bool Snappy_Uncompress(const char* input, size_t length, char* output) {
#if HAVE_SNAPPY
return snappy::RawUncompress(input, length, output);
#else
return false;
#endif // HAVE_SNAPPY
}

inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) {
return false;
}

inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) {
#if HAVE_CRC32C
return ::crc32c::Extend(crc, reinterpret_cast<const uint8_t*>(buf), size);
#else
return 0;
#endif // HAVE_CRC32C
}

} // namespace port
} // namespace leveldb

#endif // STORAGE_LEVELDB_PORT_PORT_WINDOWS_H_
Loading

0 comments on commit a5888f6

Please sign in to comment.