diff --git a/include/leveldb/db_path.h b/include/leveldb/db_path.h index 0f8ec8a..83b0f44 100644 --- a/include/leveldb/db_path.h +++ b/include/leveldb/db_path.h @@ -4,9 +4,6 @@ #include #include -#include "leveldb/export.h" -#include "leveldb/status.h" - namespace leveldb { namespace path { @@ -23,10 +20,9 @@ public: const std::string& Name() const { return path_; } const char* CName() const { return path_.c_str(); } - static bool IsDirectorySeparator(const char c); - virtual bool IsAbsolute() const = 0; virtual bool IsRelative() const = 0; + virtual size_t RootLength() = 0; inline size_t Size() const { return path_.size(); } inline bool IsEmpty() const { return path_.empty(); } @@ -38,9 +34,14 @@ protected: std::string path_; + static bool IsDirectorySeparator(const char c); + + bool StartsWith(const std::string& value, bool ignore_case = false); + virtual void Normalize() = 0; }; +// Win path class WindowsDbPath : public DbPath { public: explicit WindowsDbPath(const std::string& path) : DbPath(path) { @@ -48,13 +49,19 @@ public: } ~WindowsDbPath() {} - static bool IsValidDriveChar(const char c); - bool IsAbsolute() const override; bool IsRelative() const override; + size_t RootLength() override; + protected: + static bool IsValidDriveChar(const char c); + void Normalize() override; + +private: + const char* kExtendedPathPrefix = "\\\\?\\"; + const char* kUncExtendedPathPrefix = "\\\\?\\UNC\\"; }; diff --git a/util/db_path.cc b/util/db_path.cc index 578b6b5..9fe7e72 100644 --- a/util/db_path.cc +++ b/util/db_path.cc @@ -7,6 +7,17 @@ bool DbPath::IsDirectorySeparator(const char c) { return (c == DbPath::kDirectorySeparator || c == DbPath::kAltDirecttorySeparator); } +bool DbPath::StartsWith(const std::string& value, bool ignore_case) { + if (value.size() > path_.size()) { + return false; + } + if (ignore_case) { + auto ignore_case_cmp_func = [](char a, char b) { return std::tolower(a) == std::tolower(b); }; + return std::equal(value.begin(), value.end(), path_.begin(), ignore_case_cmp_func); + } + return std::equal(value.begin(), value.end(), path_.begin()); +} + // Windows bool WindowsDbPath::IsAbsolute() const { @@ -55,6 +66,45 @@ bool WindowsDbPath::IsValidDriveChar(const char c) { return drive_char >= 'A' && drive_char <= 'Z'; } +size_t WindowsDbPath::RootLength() { + size_t path_length = path_.size(); + size_t root_length = 0; + size_t volume_separator_length = 2; + size_t unc_root_length = 2; + + bool extended_syntax = StartsWith(kExtendedPathPrefix); + bool extended_unc_syntax = StartsWith(kUncExtendedPathPrefix); + + if (extended_syntax) { + if (extended_unc_syntax) { + unc_root_length = std::strlen(kUncExtendedPathPrefix); + } + else { + volume_separator_length += std::strlen(kExtendedPathPrefix); + } + } + + if ((!extended_syntax || extended_unc_syntax) && path_length != 0 && IsDirectorySeparator(path_[0])) { + root_length = 1; + + if (extended_unc_syntax || (path_length > 1 && IsDirectorySeparator(path_[1]))) { + root_length = unc_root_length; + int n = 2; // maximum separators to skip + while (root_length < path_length && (!IsDirectorySeparator(path_[root_length]) || --n > 0)) { + ++root_length; + } + } + } + else if (path_length >= volume_separator_length && path_[volume_separator_length - 1] == kVolumeSeparatorChar) { + root_length = volume_separator_length; + if (path_length >= volume_separator_length && IsDirectorySeparator(path_[volume_separator_length])) { + ++root_length; + } + } + + return root_length; +} + // Windows path DbPath* PathFactory::Create(const std::string& path) {