diff --git a/db/db_impl.cc b/db/db_impl.cc index 1ec2afb..e7a09af 100644 --- a/db/db_impl.cc +++ b/db/db_impl.cc @@ -37,6 +37,8 @@ namespace leveldb { +using namespace filesystem; + const int kNumNonTableCacheFiles = 10; // Information kept for every waiting writer @@ -132,6 +134,7 @@ DBImpl::DBImpl(const Options& raw_options, const std::string& dbname) owns_info_log_(options_.info_log != raw_options.info_log), owns_cache_(options_.block_cache != raw_options.block_cache), dbname_(dbname), + path_(PathFactory::Create(dbname)), table_cache_(new TableCache(dbname_, options_, TableCacheSize(options_))), db_lock_(nullptr), shutting_down_(false), @@ -176,6 +179,9 @@ DBImpl::~DBImpl() { if (owns_cache_) { delete options_.block_cache; } + if (path_ != nullptr) { + delete path_; + } } Status DBImpl::NewDB() { @@ -295,6 +301,10 @@ Status DBImpl::Recover(VersionEdit* edit, bool* save_manifest) { // Ignore error from CreateDir since the creation of the DB is // committed only when the descriptor is created, and this directory // may already exist from a previous failed creation attempt. + if (path_->IsDirectory()) { + path_->CreateDirectories(); + } + env_->CreateDir(dbname_); assert(db_lock_ == nullptr); Status s = env_->LockFile(LockFileName(dbname_), &db_lock_); diff --git a/db/db_impl.h b/db/db_impl.h index c7b0172..a14a452 100644 --- a/db/db_impl.h +++ b/db/db_impl.h @@ -17,9 +17,12 @@ #include "leveldb/env.h" #include "port/port.h" #include "port/thread_annotations.h" +#include "leveldb/filesystem.h" namespace leveldb { +using namespace filesystem; + class MemTable; class TableCache; class Version; @@ -162,7 +165,9 @@ class DBImpl : public DB { const Options options_; // options_.comparator == &internal_comparator_ const bool owns_info_log_; const bool owns_cache_; + // TODO: replace with Path; const std::string dbname_; + Path* const path_; // table_cache_ provides its own synchronization TableCache* const table_cache_; diff --git a/include/leveldb/filesystem.h b/include/leveldb/filesystem.h new file mode 100644 index 0000000..3c43e27 --- /dev/null +++ b/include/leveldb/filesystem.h @@ -0,0 +1,109 @@ +#ifndef STORAGE_LEVELDB_INCLUDE_FILE_SYSTEM_H_ +#define STORAGE_LEVELDB_INCLUDE_FILE_SYSTEM_H_ + +#include +#include + +#include "leveldb/export.h" + +namespace leveldb { + +namespace filesystem { + +bool IsDirectorySeparator(const char c); + +class Path { +public: + // Constants + static const char kDirectorySeparator = '\\'; + static const char kAltDirecttorySeparator = '/'; + static const char kVolumeSeparatorChar = ':'; + + Path() : isDir_{false}, path_("") {} + Path(const std::string& path) : path_(path) { + isDir_ = !IsEmpty() && IsDirectorySeparator(path_[Size() - 1]); + } + virtual ~Path() {} + + const std::string& ToString() const { return path_; } + const char* ToCString() const { return path_.c_str(); } + + virtual bool IsAbsolute() const = 0; + virtual bool IsRelative() const = 0; + + virtual bool CreateDirectories() = 0; + virtual bool CreateDirectory() = 0; + + inline size_t Size() const { return path_.size(); } + inline bool IsEmpty() const { return path_.empty(); } + inline bool IsDirectory() const { return isDir_; } + + // Utility functions + + inline bool HasExtension() { + if (!IsEmpty()) { + std::string::reverse_iterator& path_iter = path_.rbegin(); + + while (path_iter != path_.rend()) { + char c = *path_iter; + + if (c == '.') { + return true; + } + if (IsDirectorySeparator(c)) { + break; + } + } + } + + return false; + } + +protected: + bool isDir_; + std::string path_; + + virtual void Normalize() = 0; +}; + +#ifdef LEVELDB_PLATFORM_WINDOWS + +class WindowsFilePath : public Path { +public: + explicit WindowsFilePath(const std::string& path) : Path(path) { + Normalize(); + } + + ~WindowsFilePath() {} + + bool IsAbsolute() const override; + bool IsRelative() const override; + + bool CreateDirectories() override; + bool CreateDirectory() override; + +protected: + void Normalize() override; +}; + +inline bool IsValidDriveChar(const char c) { + const char drive_char = std::toupper(c); + return drive_char >= 'A' && drive_char <= 'Z'; +} + +#endif + +// Factory +class PathFactory { + public: + static Path* Create(const std::string& path); + + private: + PathFactory() {} + ~PathFactory() {} +}; + +} // namespace filesystem +} // namespace leveldb + +#endif // STORAGE_LEVELDB_INCLUDE_FILE_SYSTEM_H_ diff --git a/util/filesystem.cc b/util/filesystem.cc new file mode 100644 index 0000000..0c30ccb --- /dev/null +++ b/util/filesystem.cc @@ -0,0 +1,71 @@ +#include "leveldb/filesystem.h" + +namespace leveldb { +namespace filesystem { + +bool IsDirectorySeparator(const char c) { + return (c == Path::kDirectorySeparator || c == Path::kAltDirecttorySeparator); +} + +Path* PathFactory::Create(const std::string& path) +{ + #ifdef LEVELDB_PLATFORM_WINDOWS + return new WindowsFilePath(path); + #elif LEVELDB_PLATFORM_POSIX + return nullptr; + #endif + return nullptr; +} + +#ifdef LEVELDB_PLATFORM_WINDOWS + +bool WindowsFilePath::IsAbsolute() const { + return path_.size() >= 3 && IsValidDriveChar(path_[0]) && + path_[1] == Path::kVolumeSeparatorChar; +}; + +bool WindowsFilePath::IsRelative() const { + if (path_.size() < 2) { + return true; + } + + if (IsDirectorySeparator(path_[0])) { + if (path_[1] != '?') { + return !IsDirectorySeparator(path_[1]); + } + return false; + } + if (path_.size() >= 3 && path_[1] == Path::kVolumeSeparatorChar && + IsDirectorySeparator(path_[2])) { + return IsValidDriveChar(path_[0]); + } + + return true; +}; + +void WindowsFilePath::Normalize() { + auto out = path_.begin(); + + for (const char c : path_) { + if (!IsDirectorySeparator(c)) { + *(out++) = c; + } else if (out == path_.begin() || IsDirectorySeparator(*std::prev(out))) { + *(out++) = Path::kDirectorySeparator; + } else { + continue; + } + } + + path_.erase(out, path_.end()); +} + +bool WindowsFilePath::CreateDirectories() { return true; } + +bool WindowsFilePath::CreateDirectory() { return true; } + +#endif + +} +} + +