Remove/Reduce use of Regex in ObjectRegistry/Library (#9264)

Summary:
Added new ObjectLibrary::Entry classes to replace/reduce the use of Regex.  For simple factories that only do name matching, there are "StringEntry" and "AltStringEntry" classes.  For classes that use some semblance of regular expressions, there is a PatternEntry class that can match a name and prefixes.  There is also a class for Customizable::IndividualId format matches.

Added tests for the new derivative classes and got all unit tests to pass.

Resolves https://github.com/facebook/rocksdb/issues/9225.

Pull Request resolved: https://github.com/facebook/rocksdb/pull/9264

Reviewed By: pdillinger

Differential Revision: D33062001

Pulled By: mrambacher

fbshipit-source-id: c2d2143bd2d38bdf522705c8280c35381b135c03
This commit is contained in:
mrambacher 2021-12-29 07:55:17 -08:00 committed by Facebook GitHub Bot
parent 0a563ae278
commit 1c39b7952b
10 changed files with 665 additions and 193 deletions

10
env/env_encryption.cc vendored
View File

@ -1274,10 +1274,10 @@ static void RegisterEncryptionBuiltins() {
static std::once_flag once; static std::once_flag once;
std::call_once(once, [&]() { std::call_once(once, [&]() {
auto lib = ObjectRegistry::Default()->AddLibrary("encryption"); auto lib = ObjectRegistry::Default()->AddLibrary("encryption");
std::string ctr = // Match "CTR" or "CTR://test"
std::string(CTREncryptionProvider::kClassName()) + "?(://test)";
lib->Register<EncryptionProvider>( lib->Register<EncryptionProvider>(
std::string(CTREncryptionProvider::kClassName()) + "(://test)?", ObjectLibrary::PatternEntry(CTREncryptionProvider::kClassName(), true)
.AddSuffix("://test"),
[](const std::string& uri, std::unique_ptr<EncryptionProvider>* guard, [](const std::string& uri, std::unique_ptr<EncryptionProvider>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
if (EndsWith(uri, "://test")) { if (EndsWith(uri, "://test")) {
@ -1300,8 +1300,10 @@ static void RegisterEncryptionBuiltins() {
return guard->get(); return guard->get();
}); });
// Match "ROT13" or "ROT13:[0-9]+"
lib->Register<BlockCipher>( lib->Register<BlockCipher>(
std::string(ROT13BlockCipher::kClassName()) + "(:.*)?", ObjectLibrary::PatternEntry(ROT13BlockCipher::kClassName(), true)
.AddNumber(":"),
[](const std::string& uri, std::unique_ptr<BlockCipher>* guard, [](const std::string& uri, std::unique_ptr<BlockCipher>* guard,
std::string* /* errmsg */) { std::string* /* errmsg */) {
size_t colon = uri.find(':'); size_t colon = uri.find(':');

View File

@ -16,7 +16,6 @@
#include <vector> #include <vector>
#include "rocksdb/status.h" #include "rocksdb/status.h"
#include "rocksdb/utilities/regex.h"
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
class Customizable; class Customizable;
@ -43,58 +42,158 @@ using ConfigureFunc = std::function<Status(T*)>;
class ObjectLibrary { class ObjectLibrary {
public: public:
// Class for matching target strings to a pattern.
// Entries consist of a name that starts the pattern and attributes
// The following attributes can be added to the entry:
// -Suffix: Comparable to name(suffix)
// -Separator: Comparable to name(separator).+
// -Number: Comparable to name(separator).[0-9]+
// -AltName: Comparable to (name|alt)
// -Optional: Comparable to name(separator)?
// Multiple separators can be combined and cause multiple matches.
// For example, Pattern("A").AnotherName("B"),AddSeparator("@").AddNumber("#")
// is roughly equivalent to "(A|B)@.+#.+"
//
// Note that though this class does provide some regex-style matching,
// it is not a full regex parser and has some key differences:
// - Separators are matched left-most. For example, an entry
// Name("Hello").AddSeparator(" ").AddSuffix("!") would match
// "Hello world!", but not "Hello world!!"
// - No backtracking is necessary, enabling reliably efficient matching
class PatternEntry {
private:
enum Quantifier {
kMatchPattern, // [suffix].+
kMatchExact, // [suffix]
kMatchNumeric, // [suffix][0-9]+
};
public:
// Short-cut for creating an entry that matches to a
// Customizable::IndividualId
static PatternEntry AsIndividualId(const std::string& name) {
PatternEntry entry(name, true);
entry.AddSeparator("@");
entry.AddSeparator("#");
return entry;
}
// Creates a new pattern entry for "name". If optional is true,
// Matches will also return true if name==target
explicit PatternEntry(const std::string& name, bool optional = true)
: name_(name), optional_(optional), slength_(0) {
nlength_ = name_.size();
}
// Adds a suffix (exact match of separator with no trailing characters) to
// the separator
PatternEntry& AddSuffix(const std::string& suffix) {
separators_.emplace_back(suffix, kMatchExact);
slength_ += suffix.size();
return *this;
}
// Adds a separator (exact match of separator with trailing characters) to
// the entry
PatternEntry& AddSeparator(const std::string& separator) {
separators_.emplace_back(separator, kMatchPattern);
slength_ += separator.size() + 1;
return *this;
}
// Adds a separator (exact match of separator with trailing numbers) to the
// entry
PatternEntry& AddNumber(const std::string& separator) {
separators_.emplace_back(separator, kMatchNumeric);
slength_ += separator.size() + 1;
return *this;
}
// Sets another name that this entry will match, similar to (name|alt)
PatternEntry& AnotherName(const std::string& alt) {
names_.emplace_back(alt);
return *this;
}
// Sets whether the separators are required -- similar to name(separator)?
// If optional is true, then name(separator)? would match
// If optional is false, then the separators must also match
PatternEntry& SetOptional(bool optional) {
optional_ = optional;
return *this;
}
// Checks to see if the target matches this entry
bool Matches(const std::string& target) const;
const char* Name() const { return name_.c_str(); }
private:
size_t MatchSeparatorAt(size_t start, Quantifier mode,
const std::string& target, size_t tlen,
const std::string& pattern) const;
bool MatchesTarget(const std::string& name, size_t nlen,
const std::string& target, size_t ylen) const;
std::string name_; // The base name for this entry
size_t nlength_; // The length of name_
std::vector<std::string> names_; // Alternative names for this entry
bool optional_; // Whether matching of separators is required
size_t slength_; // The minimum required length to match the separators
std::vector<std::pair<std::string, Quantifier>>
separators_; // What to match
}; // End class Entry
private:
// Base class for an Entry in the Registry. // Base class for an Entry in the Registry.
class Entry { class Entry {
public: public:
virtual ~Entry() {} virtual ~Entry() {}
Entry(const std::string& name) : name_(std::move(name)) {} virtual bool Matches(const std::string& target) const = 0;
virtual const char* Name() const = 0;
// Checks to see if the target matches this entry };
virtual bool matches(const std::string& target) const {
return name_ == target;
}
const std::string& Name() const { return name_; }
private:
const std::string name_; // The name of the Entry
}; // End class Entry
// An Entry containing a FactoryFunc for creating new Objects // An Entry containing a FactoryFunc for creating new Objects
//
// !!!!!! WARNING !!!!!!: The implementation currently uses std::regex, which
// has terrible performance in some cases, including possible crash due to
// stack overflow. See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=61582
// for example. Avoid complicated regexes as much as possible.
template <typename T> template <typename T>
class FactoryEntry : public Entry { class FactoryEntry : public Entry {
public: public:
FactoryEntry(const std::string& name, FactoryFunc<T> f) FactoryEntry(const PatternEntry& e, FactoryFunc<T> f)
: Entry(name), factory_(std::move(f)) { : entry_(e), factory_(std::move(f)) {}
// FIXME: the API needs to expose this failure mode. For now, bad regexes bool Matches(const std::string& target) const override {
// will match nothing. return entry_.Matches(target);
Regex::Parse(name, &regex_).PermitUncheckedError();
}
~FactoryEntry() override {}
bool matches(const std::string& target) const override {
return regex_.Matches(target);
} }
const char* Name() const override { return entry_.Name(); }
// Creates a new T object. // Creates a new T object.
T* NewFactoryObject(const std::string& target, std::unique_ptr<T>* guard, T* NewFactoryObject(const std::string& target, std::unique_ptr<T>* guard,
std::string* msg) const { std::string* msg) const {
return factory_(target, guard, msg); return factory_(target, guard, msg);
} }
const FactoryFunc<T>& GetFactory() const { return factory_; }
private: private:
Regex regex_; // The pattern for this entry PatternEntry entry_; // The pattern for this entry
FactoryFunc<T> factory_; FactoryFunc<T> factory_;
}; // End class FactoryEntry }; // End class FactoryEntry
public: public:
explicit ObjectLibrary(const std::string& id) { id_ = id; } explicit ObjectLibrary(const std::string& id) { id_ = id; }
const std::string& GetID() const { return id_; } const std::string& GetID() const { return id_; }
// Finds the entry matching the input name and type
const Entry* FindEntry(const std::string& type, template <typename T>
const std::string& name) const; FactoryFunc<T> FindFactory(const std::string& pattern) const {
std::unique_lock<std::mutex> lock(mu_);
auto factories = factories_.find(T::Type());
if (factories != factories_.end()) {
for (const auto& e : factories->second) {
if (e->Matches(pattern)) {
const auto* fe =
static_cast<const ObjectLibrary::FactoryEntry<T>*>(e.get());
return fe->GetFactory();
}
}
}
return nullptr;
}
// Returns the total number of factories registered for this library. // Returns the total number of factories registered for this library.
// This method returns the sum of all factories registered for all types. // This method returns the sum of all factories registered for all types.
@ -108,9 +207,18 @@ class ObjectLibrary {
template <typename T> template <typename T>
const FactoryFunc<T>& Register(const std::string& pattern, const FactoryFunc<T>& Register(const std::string& pattern,
const FactoryFunc<T>& factory) { const FactoryFunc<T>& factory) {
std::unique_ptr<Entry> entry(new FactoryEntry<T>(pattern, factory)); PatternEntry entry(pattern);
AddEntry(T::Type(), entry); return Register(entry, factory);
return factory; }
template <typename T>
const FactoryFunc<T>& Register(const PatternEntry& pattern,
const FactoryFunc<T>& func) {
std::unique_ptr<Entry> entry(new FactoryEntry<T>(pattern, func));
std::unique_lock<std::mutex> lock(mu_);
auto& factories = factories_[T::Type()];
factories.emplace_back(std::move(entry));
return func;
} }
// Invokes the registrar function with the supplied arg for this library. // Invokes the registrar function with the supplied arg for this library.
@ -122,13 +230,11 @@ class ObjectLibrary {
static std::shared_ptr<ObjectLibrary>& Default(); static std::shared_ptr<ObjectLibrary>& Default();
private: private:
// Adds the input entry to the list for the given type
void AddEntry(const std::string& type, std::unique_ptr<Entry>& entry);
// Protects the entry map // Protects the entry map
mutable std::mutex mu_; mutable std::mutex mu_;
// ** FactoryFunctions for this loader, organized by type // ** FactoryFunctions for this loader, organized by type
std::unordered_map<std::string, std::vector<std::unique_ptr<Entry>>> entries_; std::unordered_map<std::string, std::vector<std::unique_ptr<Entry>>>
factories_;
// The name for this library // The name for this library
std::string id_; std::string id_;
@ -178,11 +284,9 @@ class ObjectRegistry {
T* NewObject(const std::string& target, std::unique_ptr<T>* guard, T* NewObject(const std::string& target, std::unique_ptr<T>* guard,
std::string* errmsg) { std::string* errmsg) {
guard->reset(); guard->reset();
const auto* basic = FindEntry(T::Type(), target); auto factory = FindFactory<T>(target);
if (basic != nullptr) { if (factory != nullptr) {
const auto* factory = return factory(target, guard, errmsg);
static_cast<const ObjectLibrary::FactoryEntry<T>*>(basic);
return factory->NewFactoryObject(target, guard, errmsg);
} else { } else {
*errmsg = std::string("Could not load ") + T::Type(); *errmsg = std::string("Could not load ") + T::Type();
return nullptr; return nullptr;
@ -386,8 +490,27 @@ class ObjectRegistry {
Status SetManagedObject(const std::string& type, const std::string& id, Status SetManagedObject(const std::string& type, const std::string& id,
const std::shared_ptr<Customizable>& c); const std::shared_ptr<Customizable>& c);
const ObjectLibrary::Entry* FindEntry(const std::string& type, // Searches (from back to front) the libraries looking for the
const std::string& name) const; // factory that matches this pattern.
// Returns the factory if it is found, and nullptr otherwise
template <typename T>
const FactoryFunc<T> FindFactory(const std::string& name) const {
{
std::unique_lock<std::mutex> lock(library_mutex_);
for (auto iter = libraries_.crbegin(); iter != libraries_.crend();
++iter) {
const auto factory = iter->get()->FindFactory<T>(name);
if (factory != nullptr) {
return factory;
}
}
}
if (parent_ != nullptr) {
return parent_->FindFactory<T>(name);
} else {
return nullptr;
}
}
// The set of libraries to search for factories for this registry. // The set of libraries to search for factories for this registry.
// The libraries are searched in reverse order (back to front) when // The libraries are searched in reverse order (back to front) when

View File

@ -41,6 +41,10 @@
#include "util/string_util.h" #include "util/string_util.h"
#include "utilities/compaction_filters/remove_emptyvalue_compactionfilter.h" #include "utilities/compaction_filters/remove_emptyvalue_compactionfilter.h"
#include "utilities/memory_allocators.h" #include "utilities/memory_allocators.h"
#include "utilities/merge_operators/bytesxor.h"
#include "utilities/merge_operators/sortlist.h"
#include "utilities/merge_operators/string_append/stringappend.h"
#include "utilities/merge_operators/string_append/stringappend2.h"
#ifndef GFLAGS #ifndef GFLAGS
bool FLAGS_enable_print = false; bool FLAGS_enable_print = false;
@ -177,7 +181,7 @@ static int A_count = 0;
static int RegisterCustomTestObjects(ObjectLibrary& library, static int RegisterCustomTestObjects(ObjectLibrary& library,
const std::string& /*arg*/) { const std::string& /*arg*/) {
library.Register<TestCustomizable>( library.Register<TestCustomizable>(
"A.*", ObjectLibrary::PatternEntry("A", true).AddSeparator("_"),
[](const std::string& name, std::unique_ptr<TestCustomizable>* guard, [](const std::string& name, std::unique_ptr<TestCustomizable>* guard,
std::string* /* msg */) { std::string* /* msg */) {
guard->reset(new ACustomizable(name)); guard->reset(new ACustomizable(name));
@ -322,7 +326,7 @@ class CustomizableTest : public testing::Test {
// - a property with a name // - a property with a name
TEST_F(CustomizableTest, CreateByNameTest) { TEST_F(CustomizableTest, CreateByNameTest) {
ObjectLibrary::Default()->Register<TestCustomizable>( ObjectLibrary::Default()->Register<TestCustomizable>(
"TEST.*", ObjectLibrary::PatternEntry("TEST", false).AddSeparator("_"),
[](const std::string& name, std::unique_ptr<TestCustomizable>* guard, [](const std::string& name, std::unique_ptr<TestCustomizable>* guard,
std::string* /* msg */) { std::string* /* msg */) {
guard->reset(new TestCustomizable(name)); guard->reset(new TestCustomizable(name));
@ -931,12 +935,12 @@ TEST_F(CustomizableTest, NoNameTest) {
auto copts = copy.GetOptions<SimpleOptions>(); auto copts = copy.GetOptions<SimpleOptions>();
sopts->cu.reset(new ACustomizable("")); sopts->cu.reset(new ACustomizable(""));
orig.cv.push_back(std::make_shared<ACustomizable>("")); orig.cv.push_back(std::make_shared<ACustomizable>(""));
orig.cv.push_back(std::make_shared<ACustomizable>("A1")); orig.cv.push_back(std::make_shared<ACustomizable>("A_1"));
std::string opt_str, mismatch; std::string opt_str, mismatch;
ASSERT_OK(orig.GetOptionString(config_options_, &opt_str)); ASSERT_OK(orig.GetOptionString(config_options_, &opt_str));
ASSERT_OK(copy.ConfigureFromString(config_options_, opt_str)); ASSERT_OK(copy.ConfigureFromString(config_options_, opt_str));
ASSERT_EQ(copy.cv.size(), 1U); ASSERT_EQ(copy.cv.size(), 1U);
ASSERT_EQ(copy.cv[0]->GetId(), "A1"); ASSERT_EQ(copy.cv[0]->GetId(), "A_1");
ASSERT_EQ(copts->cu, nullptr); ASSERT_EQ(copts->cu, nullptr);
} }
@ -1016,19 +1020,27 @@ TEST_F(CustomizableTest, FactoryFunctionTest) {
TEST_F(CustomizableTest, URLFactoryTest) { TEST_F(CustomizableTest, URLFactoryTest) {
std::unique_ptr<TestCustomizable> unique; std::unique_ptr<TestCustomizable> unique;
config_options_.registry->AddLibrary("URL")->Register<TestCustomizable>(
ObjectLibrary::PatternEntry("Z", false).AddSeparator(""),
[](const std::string& name, std::unique_ptr<TestCustomizable>* guard,
std::string* /* msg */) {
guard->reset(new TestCustomizable(name));
return guard->get();
});
ConfigOptions ignore = config_options_; ConfigOptions ignore = config_options_;
ignore.ignore_unsupported_options = false; ignore.ignore_unsupported_options = false;
ignore.ignore_unsupported_options = false; ignore.ignore_unsupported_options = false;
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "A=1;x=y", &unique)); ASSERT_OK(TestCustomizable::CreateFromString(ignore, "Z=1;x=y", &unique));
ASSERT_NE(unique, nullptr); ASSERT_NE(unique, nullptr);
ASSERT_EQ(unique->GetId(), "A=1;x=y"); ASSERT_EQ(unique->GetId(), "Z=1;x=y");
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "A;x=y", &unique)); ASSERT_OK(TestCustomizable::CreateFromString(ignore, "Z;x=y", &unique));
ASSERT_NE(unique, nullptr); ASSERT_NE(unique, nullptr);
ASSERT_EQ(unique->GetId(), "A;x=y"); ASSERT_EQ(unique->GetId(), "Z;x=y");
unique.reset(); unique.reset();
ASSERT_OK(TestCustomizable::CreateFromString(ignore, "A=1?x=y", &unique)); ASSERT_OK(TestCustomizable::CreateFromString(ignore, "Z=1?x=y", &unique));
ASSERT_NE(unique, nullptr); ASSERT_NE(unique, nullptr);
ASSERT_EQ(unique->GetId(), "A=1?x=y"); ASSERT_EQ(unique->GetId(), "Z=1?x=y");
} }
TEST_F(CustomizableTest, MutableOptionsTest) { TEST_F(CustomizableTest, MutableOptionsTest) {
@ -1126,6 +1138,7 @@ TEST_F(CustomizableTest, CustomManagedObjects) {
std::shared_ptr<TestCustomizable> object1, object2; std::shared_ptr<TestCustomizable> object1, object2;
ASSERT_OK(LoadManagedObject<TestCustomizable>( ASSERT_OK(LoadManagedObject<TestCustomizable>(
config_options_, "id=A_1;int=1;bool=true", &object1)); config_options_, "id=A_1;int=1;bool=true", &object1));
ASSERT_NE(object1, nullptr);
ASSERT_OK( ASSERT_OK(
LoadManagedObject<TestCustomizable>(config_options_, "A_1", &object2)); LoadManagedObject<TestCustomizable>(config_options_, "A_1", &object2));
ASSERT_EQ(object1, object2); ASSERT_EQ(object1, object2);
@ -1165,9 +1178,11 @@ TEST_F(CustomizableTest, CreateManagedObjects) {
config_options_.registry->AddLibrary("Managed") config_options_.registry->AddLibrary("Managed")
->Register<ManagedCustomizable>( ->Register<ManagedCustomizable>(
"Managed(@.*)?", [](const std::string& /*name*/, ObjectLibrary::PatternEntry::AsIndividualId(
std::unique_ptr<ManagedCustomizable>* guard, ManagedCustomizable::kClassName()),
std::string* /* msg */) { [](const std::string& /*name*/,
std::unique_ptr<ManagedCustomizable>* guard,
std::string* /* msg */) {
guard->reset(new ManagedCustomizable()); guard->reset(new ManagedCustomizable());
return guard->get(); return guard->get();
}); });
@ -1317,7 +1332,8 @@ class MockMemoryAllocator : public BaseMemoryAllocator {
class MockEncryptionProvider : public EncryptionProvider { class MockEncryptionProvider : public EncryptionProvider {
public: public:
explicit MockEncryptionProvider(const std::string& id) : id_(id) {} explicit MockEncryptionProvider(const std::string& id) : id_(id) {}
const char* Name() const override { return "Mock"; } static const char* kClassName() { return "Mock"; }
const char* Name() const override { return kClassName(); }
size_t GetPrefixLength() const override { return 0; } size_t GetPrefixLength() const override { return 0; }
Status CreateNewPrefix(const std::string& /*fname*/, char* /*prefix*/, Status CreateNewPrefix(const std::string& /*fname*/, char* /*prefix*/,
size_t /*prefixLength*/) const override { size_t /*prefixLength*/) const override {
@ -1459,7 +1475,8 @@ static int RegisterLocalObjects(ObjectLibrary& library,
}); });
library.Register<EncryptionProvider>( library.Register<EncryptionProvider>(
"Mock(://test)?", ObjectLibrary::PatternEntry(MockEncryptionProvider::kClassName(), true)
.AddSuffix("://test"),
[](const std::string& uri, std::unique_ptr<EncryptionProvider>* guard, [](const std::string& uri, std::unique_ptr<EncryptionProvider>* guard,
std::string* /* errmsg */) { std::string* /* errmsg */) {
guard->reset(new MockEncryptionProvider(uri)); guard->reset(new MockEncryptionProvider(uri));
@ -1811,9 +1828,74 @@ TEST_F(LoadCustomizableTest, LoadMergeOperatorTest) {
ASSERT_NOK( ASSERT_NOK(
MergeOperator::CreateFromString(config_options_, "Changling", &result)); MergeOperator::CreateFromString(config_options_, "Changling", &result));
//**TODO: MJR: Use the constants when these names are in public classes
ASSERT_OK(MergeOperator::CreateFromString(config_options_, "put", &result)); ASSERT_OK(MergeOperator::CreateFromString(config_options_, "put", &result));
ASSERT_NE(result, nullptr); ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), "PutOperator"); ASSERT_STREQ(result->Name(), "PutOperator");
ASSERT_OK(
MergeOperator::CreateFromString(config_options_, "PutOperator", &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), "PutOperator");
ASSERT_OK(
MergeOperator::CreateFromString(config_options_, "put_v1", &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), "PutOperator");
ASSERT_OK(
MergeOperator::CreateFromString(config_options_, "uint64add", &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), "UInt64AddOperator");
ASSERT_OK(MergeOperator::CreateFromString(config_options_,
"UInt64AddOperator", &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), "UInt64AddOperator");
ASSERT_OK(MergeOperator::CreateFromString(config_options_, "max", &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), "MaxOperator");
ASSERT_OK(
MergeOperator::CreateFromString(config_options_, "MaxOperator", &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), "MaxOperator");
#ifndef ROCKSDB_LITE
ASSERT_OK(MergeOperator::CreateFromString(
config_options_, StringAppendOperator::kNickName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), StringAppendOperator::kClassName());
ASSERT_OK(MergeOperator::CreateFromString(
config_options_, StringAppendOperator::kClassName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), StringAppendOperator::kClassName());
ASSERT_OK(MergeOperator::CreateFromString(
config_options_, StringAppendTESTOperator::kNickName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), StringAppendTESTOperator::kClassName());
ASSERT_OK(MergeOperator::CreateFromString(
config_options_, StringAppendTESTOperator::kClassName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), StringAppendTESTOperator::kClassName());
ASSERT_OK(MergeOperator::CreateFromString(config_options_,
SortList::kNickName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), SortList::kClassName());
ASSERT_OK(MergeOperator::CreateFromString(config_options_,
SortList::kClassName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), SortList::kClassName());
ASSERT_OK(MergeOperator::CreateFromString(
config_options_, BytesXOROperator::kNickName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), BytesXOROperator::kClassName());
ASSERT_OK(MergeOperator::CreateFromString(
config_options_, BytesXOROperator::kClassName(), &result));
ASSERT_NE(result, nullptr);
ASSERT_STREQ(result->Name(), BytesXOROperator::kClassName());
#endif // ROCKSDB_LITE
ASSERT_NOK(
MergeOperator::CreateFromString(config_options_, "Changling", &result));
if (RegisterTests("Test")) { if (RegisterTests("Test")) {
ASSERT_OK( ASSERT_OK(
MergeOperator::CreateFromString(config_options_, "Changling", &result)); MergeOperator::CreateFromString(config_options_, "Changling", &result));

View File

@ -2079,8 +2079,9 @@ TEST_F(OptionsTest, OptionTablePropertiesTest) {
// properties as the original // properties as the original
cfg_opts.registry->AddLibrary("collector") cfg_opts.registry->AddLibrary("collector")
->Register<TablePropertiesCollectorFactory>( ->Register<TablePropertiesCollectorFactory>(
std::string(TestTablePropertiesCollectorFactory::kClassName()) + ObjectLibrary::PatternEntry(
":.*", TestTablePropertiesCollectorFactory::kClassName(), false)
.AddSeparator(":"),
[](const std::string& name, [](const std::string& name,
std::unique_ptr<TablePropertiesCollectorFactory>* guard, std::unique_ptr<TablePropertiesCollectorFactory>* guard,
std::string* /* errmsg */) { std::string* /* errmsg */) {

View File

@ -161,15 +161,14 @@ static int RegisterBuiltinMemTableRepFactory(ObjectLibrary& library,
// The MemTableRepFactory built-in classes will be either a class // The MemTableRepFactory built-in classes will be either a class
// (VectorRepFactory) or a nickname (vector), followed optionally by ":#", // (VectorRepFactory) or a nickname (vector), followed optionally by ":#",
// where # is the "size" of the factory. // where # is the "size" of the factory.
auto AsRegex = [](const std::string& name, const std::string& alt) { auto AsPattern = [](const std::string& name, const std::string& alt) {
std::string regex; auto pattern = ObjectLibrary::PatternEntry(name, true);
regex.append("(").append(name); pattern.AnotherName(alt);
regex.append("|").append(alt).append(")(:[0-9]*)?"); pattern.AddNumber(":");
return regex; return pattern;
}; };
library.Register<MemTableRepFactory>( library.Register<MemTableRepFactory>(
AsRegex(VectorRepFactory::kClassName(), VectorRepFactory::kNickName()), AsPattern(VectorRepFactory::kClassName(), VectorRepFactory::kNickName()),
[](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard, [](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
auto colon = uri.find(":"); auto colon = uri.find(":");
@ -182,7 +181,7 @@ static int RegisterBuiltinMemTableRepFactory(ObjectLibrary& library,
return guard->get(); return guard->get();
}); });
library.Register<MemTableRepFactory>( library.Register<MemTableRepFactory>(
AsRegex(SkipListFactory::kClassName(), SkipListFactory::kNickName()), AsPattern(SkipListFactory::kClassName(), SkipListFactory::kNickName()),
[](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard, [](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
auto colon = uri.find(":"); auto colon = uri.find(":");
@ -195,7 +194,7 @@ static int RegisterBuiltinMemTableRepFactory(ObjectLibrary& library,
return guard->get(); return guard->get();
}); });
library.Register<MemTableRepFactory>( library.Register<MemTableRepFactory>(
AsRegex("HashLinkListRepFactory", "hash_linkedlist"), AsPattern("HashLinkListRepFactory", "hash_linkedlist"),
[](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard, [](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
// Expecting format: hash_linkedlist:<hash_bucket_count> // Expecting format: hash_linkedlist:<hash_bucket_count>
@ -209,7 +208,7 @@ static int RegisterBuiltinMemTableRepFactory(ObjectLibrary& library,
return guard->get(); return guard->get();
}); });
library.Register<MemTableRepFactory>( library.Register<MemTableRepFactory>(
AsRegex("HashSkipListRepFactory", "prefix_hash"), AsPattern("HashSkipListRepFactory", "prefix_hash"),
[](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard, [](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
// Expecting format: prefix_hash:<hash_bucket_count> // Expecting format: prefix_hash:<hash_bucket_count>
@ -230,9 +229,11 @@ static int RegisterBuiltinMemTableRepFactory(ObjectLibrary& library,
return nullptr; return nullptr;
}); });
return 5; size_t num_types;
return static_cast<int>(library.GetFactoryCount(&num_types));
} }
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE
Status GetMemTableRepFactoryFromString( Status GetMemTableRepFactoryFromString(
const std::string& opts_str, std::unique_ptr<MemTableRepFactory>* result) { const std::string& opts_str, std::unique_ptr<MemTableRepFactory>* result) {
ConfigOptions config_options; ConfigOptions config_options;

View File

@ -676,6 +676,25 @@ class SpecialMemTableRep : public MemTableRep {
}; };
class SpecialSkipListFactory : public MemTableRepFactory { class SpecialSkipListFactory : public MemTableRepFactory {
public: public:
#ifndef ROCKSDB_LITE
static bool Register(ObjectLibrary& library, const std::string& /*arg*/) {
library.Register<MemTableRepFactory>(
ObjectLibrary::PatternEntry(SpecialSkipListFactory::kClassName(), true)
.AddNumber(":"),
[](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard,
std::string* /* errmsg */) {
auto colon = uri.find(":");
if (colon != std::string::npos) {
auto count = ParseInt(uri.substr(colon + 1));
guard->reset(new SpecialSkipListFactory(count));
} else {
guard->reset(new SpecialSkipListFactory(2));
}
return guard->get();
});
return true;
}
#endif // ROCKSDB_LITE
// After number of inserts exceeds `num_entries_flush` in a mem table, trigger // After number of inserts exceeds `num_entries_flush` in a mem table, trigger
// flush. // flush.
explicit SpecialSkipListFactory(int num_entries_flush) explicit SpecialSkipListFactory(int num_entries_flush)
@ -717,7 +736,7 @@ MemTableRepFactory* NewSpecialSkipListFactory(int num_entries_per_flush) {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
// This method loads existing test classes into the ObjectRegistry // This method loads existing test classes into the ObjectRegistry
int RegisterTestObjects(ObjectLibrary& library, const std::string& /*arg*/) { int RegisterTestObjects(ObjectLibrary& library, const std::string& arg) {
size_t num_types; size_t num_types;
library.Register<const Comparator>( library.Register<const Comparator>(
test::SimpleSuffixReverseComparator::kClassName(), test::SimpleSuffixReverseComparator::kClassName(),
@ -727,19 +746,7 @@ int RegisterTestObjects(ObjectLibrary& library, const std::string& /*arg*/) {
static test::SimpleSuffixReverseComparator ssrc; static test::SimpleSuffixReverseComparator ssrc;
return &ssrc; return &ssrc;
}); });
library.Register<MemTableRepFactory>( SpecialSkipListFactory::Register(library, arg);
std::string(SpecialSkipListFactory::kClassName()) + "(:[0-9]*)?",
[](const std::string& uri, std::unique_ptr<MemTableRepFactory>* guard,
std::string* /* errmsg */) {
auto colon = uri.find(":");
if (colon != std::string::npos) {
auto count = ParseInt(uri.substr(colon + 1));
guard->reset(new SpecialSkipListFactory(count));
} else {
guard->reset(new SpecialSkipListFactory(2));
}
return guard->get();
});
library.Register<MergeOperator>( library.Register<MergeOperator>(
"Changling", "Changling",
[](const std::string& uri, std::unique_ptr<MergeOperator>* guard, [](const std::string& uri, std::unique_ptr<MergeOperator>* guard,

View File

@ -143,6 +143,9 @@ const SliceTransform* NewNoopTransform() { return new NoopTransform; }
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
static int RegisterBuiltinSliceTransform(ObjectLibrary& library, static int RegisterBuiltinSliceTransform(ObjectLibrary& library,
const std::string& /*arg*/) { const std::string& /*arg*/) {
// For the builtin transforms, the format is typically
// [Name] or [Name].[0-9]+
// [NickName]:[0-9]+
library.Register<const SliceTransform>( library.Register<const SliceTransform>(
NoopTransform::kClassName(), NoopTransform::kClassName(),
[](const std::string& /*uri*/, [](const std::string& /*uri*/,
@ -152,7 +155,8 @@ static int RegisterBuiltinSliceTransform(ObjectLibrary& library,
return guard->get(); return guard->get();
}); });
library.Register<const SliceTransform>( library.Register<const SliceTransform>(
std::string(FixedPrefixTransform::kNickName()) + ":[0-9]+", ObjectLibrary::PatternEntry(FixedPrefixTransform::kNickName(), false)
.AddNumber(":"),
[](const std::string& uri, std::unique_ptr<const SliceTransform>* guard, [](const std::string& uri, std::unique_ptr<const SliceTransform>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
auto colon = uri.find(":"); auto colon = uri.find(":");
@ -161,24 +165,22 @@ static int RegisterBuiltinSliceTransform(ObjectLibrary& library,
return guard->get(); return guard->get();
}); });
library.Register<const SliceTransform>( library.Register<const SliceTransform>(
FixedPrefixTransform::kClassName(), ObjectLibrary::PatternEntry(FixedPrefixTransform::kClassName(), true)
[](const std::string& /*uri*/, .AddNumber("."),
std::unique_ptr<const SliceTransform>* guard,
std::string* /*errmsg*/) {
guard->reset(NewFixedPrefixTransform(0));
return guard->get();
});
library.Register<const SliceTransform>(
std::string(FixedPrefixTransform::kClassName()) + "\\.[0-9]+",
[](const std::string& uri, std::unique_ptr<const SliceTransform>* guard, [](const std::string& uri, std::unique_ptr<const SliceTransform>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
auto len = ParseSizeT( if (uri == FixedPrefixTransform::kClassName()) {
uri.substr(strlen(FixedPrefixTransform::kClassName()) + 1)); guard->reset(NewFixedPrefixTransform(0));
guard->reset(NewFixedPrefixTransform(len)); } else {
auto len = ParseSizeT(
uri.substr(strlen(FixedPrefixTransform::kClassName()) + 1));
guard->reset(NewFixedPrefixTransform(len));
}
return guard->get(); return guard->get();
}); });
library.Register<const SliceTransform>( library.Register<const SliceTransform>(
std::string(CappedPrefixTransform::kNickName()) + ":[0-9]+", ObjectLibrary::PatternEntry(CappedPrefixTransform::kNickName(), false)
.AddNumber(":"),
[](const std::string& uri, std::unique_ptr<const SliceTransform>* guard, [](const std::string& uri, std::unique_ptr<const SliceTransform>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
auto colon = uri.find(":"); auto colon = uri.find(":");
@ -187,19 +189,21 @@ static int RegisterBuiltinSliceTransform(ObjectLibrary& library,
return guard->get(); return guard->get();
}); });
library.Register<const SliceTransform>( library.Register<const SliceTransform>(
std::string(CappedPrefixTransform::kClassName()) + "(\\.[0-9]+)?", ObjectLibrary::PatternEntry(CappedPrefixTransform::kClassName(), true)
.AddNumber("."),
[](const std::string& uri, std::unique_ptr<const SliceTransform>* guard, [](const std::string& uri, std::unique_ptr<const SliceTransform>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
if (uri == CappedPrefixTransform::kClassName()) { if (uri == CappedPrefixTransform::kClassName()) {
guard->reset(NewCappedPrefixTransform(0)); guard->reset(NewCappedPrefixTransform(0));
} else { // Length + "." } else {
auto len = ParseSizeT( auto len = ParseSizeT(
uri.substr(strlen(CappedPrefixTransform::kClassName()) + 1)); uri.substr(strlen(CappedPrefixTransform::kClassName()) + 1));
guard->reset(NewCappedPrefixTransform(len)); guard->reset(NewCappedPrefixTransform(len));
} }
return guard->get(); return guard->get();
}); });
return 5; size_t num_types;
return static_cast<int>(library.GetFactoryCount(&num_types));
} }
#endif // ROCKSDB_LITE #endif // ROCKSDB_LITE

View File

@ -55,38 +55,33 @@ static bool LoadMergeOperator(const std::string& id,
static int RegisterBuiltinMergeOperators(ObjectLibrary& library, static int RegisterBuiltinMergeOperators(ObjectLibrary& library,
const std::string& /*arg*/) { const std::string& /*arg*/) {
size_t num_types; size_t num_types;
auto AsRegex = [](const std::string& name, const std::string& alt) {
std::string regex;
regex.append("(").append(name);
regex.append("|").append(alt).append(")");
return regex;
};
library.Register<MergeOperator>( library.Register<MergeOperator>(
AsRegex(StringAppendOperator::kClassName(), ObjectLibrary::PatternEntry(StringAppendOperator::kClassName())
StringAppendOperator::kNickName()), .AnotherName(StringAppendOperator::kNickName()),
[](const std::string& /*uri*/, std::unique_ptr<MergeOperator>* guard, [](const std::string& /*uri*/, std::unique_ptr<MergeOperator>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
guard->reset(new StringAppendOperator(",")); guard->reset(new StringAppendOperator(","));
return guard->get(); return guard->get();
}); });
library.Register<MergeOperator>( library.Register<MergeOperator>(
AsRegex(StringAppendTESTOperator::kClassName(), ObjectLibrary::PatternEntry(StringAppendTESTOperator::kClassName())
StringAppendTESTOperator::kNickName()), .AnotherName(StringAppendTESTOperator::kNickName()),
[](const std::string& /*uri*/, std::unique_ptr<MergeOperator>* guard, [](const std::string& /*uri*/, std::unique_ptr<MergeOperator>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
guard->reset(new StringAppendTESTOperator(",")); guard->reset(new StringAppendTESTOperator(","));
return guard->get(); return guard->get();
}); });
library.Register<MergeOperator>( library.Register<MergeOperator>(
AsRegex(SortList::kClassName(), SortList::kNickName()), ObjectLibrary::PatternEntry(SortList::kClassName())
.AnotherName(SortList::kNickName()),
[](const std::string& /*uri*/, std::unique_ptr<MergeOperator>* guard, [](const std::string& /*uri*/, std::unique_ptr<MergeOperator>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
guard->reset(new SortList()); guard->reset(new SortList());
return guard->get(); return guard->get();
}); });
library.Register<MergeOperator>( library.Register<MergeOperator>(
AsRegex(BytesXOROperator::kClassName(), BytesXOROperator::kNickName()), ObjectLibrary::PatternEntry(BytesXOROperator::kClassName())
.AnotherName(BytesXOROperator::kNickName()),
[](const std::string& /*uri*/, std::unique_ptr<MergeOperator>* guard, [](const std::string& /*uri*/, std::unique_ptr<MergeOperator>* guard,
std::string* /*errmsg*/) { std::string* /*errmsg*/) {
guard->reset(new BytesXOROperator()); guard->reset(new BytesXOROperator());

View File

@ -5,6 +5,8 @@
#include "rocksdb/utilities/object_registry.h" #include "rocksdb/utilities/object_registry.h"
#include <ctype.h>
#include "logging/logging.h" #include "logging/logging.h"
#include "rocksdb/customizable.h" #include "rocksdb/customizable.h"
#include "rocksdb/env.h" #include "rocksdb/env.h"
@ -12,35 +14,105 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
#ifndef ROCKSDB_LITE #ifndef ROCKSDB_LITE
// Looks through the "type" factories for one that matches "name". size_t ObjectLibrary::PatternEntry::MatchSeparatorAt(
// If found, returns the pointer to the Entry matching this name. size_t start, Quantifier mode, const std::string &target, size_t tlen,
// Otherwise, nullptr is returned const std::string &separator) const {
const ObjectLibrary::Entry *ObjectLibrary::FindEntry( size_t slen = separator.size();
const std::string &type, const std::string &name) const { // See if there is enough space. If so, find the separator
std::unique_lock<std::mutex> lock(mu_); if (tlen < start + slen) {
auto entries = entries_.find(type); return std::string::npos; // not enough space left
if (entries != entries_.end()) { } else if (mode == kMatchExact) {
for (const auto &entry : entries->second) { // Exact mode means the next thing we are looking for is the separator
if (entry->matches(name)) { if (target.compare(start, slen, separator) != 0) {
return entry.get(); return std::string::npos;
} else {
return start + slen; // Found the separator, return where we found it
}
} else {
auto pos = start + 1;
if (!separator.empty()) {
pos = target.find(separator, pos);
}
if (pos == std::string::npos) {
return pos;
} else if (mode == kMatchNumeric) {
// If it is numeric, everything up to the match must be a number
while (start < pos) {
if (!isdigit(target[start++])) {
return std::string::npos;
}
}
}
return pos + slen;
}
}
bool ObjectLibrary::PatternEntry::MatchesTarget(const std::string &name,
size_t nlen,
const std::string &target,
size_t tlen) const {
if (separators_.empty()) {
assert(optional_); // If there are no separators, it must be only a name
return nlen == tlen && name == target;
} else if (nlen == tlen) { // The lengths are the same
return optional_ && name == target;
} else if (tlen < nlen + slength_) {
// The target is not long enough
return false;
} else if (target.compare(0, nlen, name) != 0) {
return false; // Target does not start with name
} else {
// Loop through all of the separators one at a time matching them.
// Note that we first match the separator and then its quantifiers.
// Since we expect the separator first, we start with an exact match
// Subsequent matches will use the quantifier of the previous separator
size_t start = nlen;
auto mode = kMatchExact;
for (size_t idx = 0; idx < separators_.size(); ++idx) {
const auto &separator = separators_[idx];
start = MatchSeparatorAt(start, mode, target, tlen, separator.first);
if (start == std::string::npos) {
return false;
} else {
mode = separator.second;
}
}
// We have matched all of the separators. Now check that what is left
// unmatched in the target is acceptable.
if (mode == kMatchExact) {
return (start == tlen);
} else if (start >= tlen) {
return false;
} else if (mode == kMatchNumeric) {
while (start < tlen) {
if (!isdigit(target[start++])) {
return false;
}
} }
} }
} }
return nullptr; return true;
} }
void ObjectLibrary::AddEntry(const std::string &type, bool ObjectLibrary::PatternEntry::Matches(const std::string &target) const {
std::unique_ptr<Entry> &entry) { auto tlen = target.size();
std::unique_lock<std::mutex> lock(mu_); if (MatchesTarget(name_, nlength_, target, tlen)) {
auto &entries = entries_[type]; return true;
entries.emplace_back(std::move(entry)); } else if (!names_.empty()) {
for (const auto &alt : names_) {
if (MatchesTarget(alt, alt.size(), target, tlen)) {
return true;
}
}
}
return false;
} }
size_t ObjectLibrary::GetFactoryCount(size_t *types) const { size_t ObjectLibrary::GetFactoryCount(size_t *types) const {
std::unique_lock<std::mutex> lock(mu_); std::unique_lock<std::mutex> lock(mu_);
*types = entries_.size(); *types = factories_.size();
size_t factories = 0; size_t factories = 0;
for (const auto &e : entries_) { for (const auto &e : factories_) {
factories += e.second.size(); factories += e.second.size();
} }
return factories; return factories;
@ -48,13 +120,12 @@ size_t ObjectLibrary::GetFactoryCount(size_t *types) const {
void ObjectLibrary::Dump(Logger *logger) const { void ObjectLibrary::Dump(Logger *logger) const {
std::unique_lock<std::mutex> lock(mu_); std::unique_lock<std::mutex> lock(mu_);
for (const auto &iter : entries_) { for (const auto &iter : factories_) {
ROCKS_LOG_HEADER(logger, " Registered factories for type[%s] ", ROCKS_LOG_HEADER(logger, " Registered factories for type[%s] ",
iter.first.c_str()); iter.first.c_str());
bool printed_one = false; bool printed_one = false;
for (const auto &e : iter.second) { for (const auto &e : iter.second) {
ROCKS_LOG_HEADER(logger, "%c %s", (printed_one) ? ',' : ':', ROCKS_LOG_HEADER(logger, "%c %s", (printed_one) ? ',' : ':', e->Name());
e->Name().c_str());
printed_one = true; printed_one = true;
} }
} }
@ -84,26 +155,6 @@ std::shared_ptr<ObjectRegistry> ObjectRegistry::NewInstance(
return std::make_shared<ObjectRegistry>(parent); return std::make_shared<ObjectRegistry>(parent);
} }
// Searches (from back to front) the libraries looking for the
// an entry that matches this pattern.
// Returns the entry if it is found, and nullptr otherwise
const ObjectLibrary::Entry *ObjectRegistry::FindEntry(
const std::string &type, const std::string &name) const {
{
std::unique_lock<std::mutex> lock(library_mutex_);
for (auto iter = libraries_.crbegin(); iter != libraries_.crend(); ++iter) {
const auto *entry = iter->get()->FindEntry(type, name);
if (entry != nullptr) {
return entry;
}
}
}
if (parent_ != nullptr) {
return parent_->FindEntry(type, name);
} else {
return nullptr;
}
}
Status ObjectRegistry::SetManagedObject( Status ObjectRegistry::SetManagedObject(
const std::string &type, const std::string &id, const std::string &type, const std::string &id,
const std::shared_ptr<Customizable> &object) { const std::shared_ptr<Customizable> &object) {

View File

@ -12,32 +12,33 @@
namespace ROCKSDB_NAMESPACE { namespace ROCKSDB_NAMESPACE {
class EnvRegistryTest : public testing::Test { class ObjRegistryTest : public testing::Test {
public: public:
static int num_a, num_b; static int num_a, num_b;
}; };
int EnvRegistryTest::num_a = 0; int ObjRegistryTest::num_a = 0;
int EnvRegistryTest::num_b = 0; int ObjRegistryTest::num_b = 0;
static FactoryFunc<Env> test_reg_a = ObjectLibrary::Default()->Register<Env>( static FactoryFunc<Env> test_reg_a = ObjectLibrary::Default()->Register<Env>(
"a://.*", ObjectLibrary::PatternEntry("a", false).AddSeparator("://"),
[](const std::string& /*uri*/, std::unique_ptr<Env>* /*env_guard*/, [](const std::string& /*uri*/, std::unique_ptr<Env>* /*env_guard*/,
std::string* /* errmsg */) { std::string* /* errmsg */) {
++EnvRegistryTest::num_a; ++ObjRegistryTest::num_a;
return Env::Default(); return Env::Default();
}); });
static FactoryFunc<Env> test_reg_b = ObjectLibrary::Default()->Register<Env>( static FactoryFunc<Env> test_reg_b = ObjectLibrary::Default()->Register<Env>(
"b://.*", [](const std::string& /*uri*/, std::unique_ptr<Env>* env_guard, ObjectLibrary::PatternEntry("b", false).AddSeparator("://"),
std::string* /* errmsg */) { [](const std::string& /*uri*/, std::unique_ptr<Env>* env_guard,
++EnvRegistryTest::num_b; std::string* /* errmsg */) {
++ObjRegistryTest::num_b;
// Env::Default() is a singleton so we can't grant ownership directly to // Env::Default() is a singleton so we can't grant ownership directly to
// the caller - we must wrap it first. // the caller - we must wrap it first.
env_guard->reset(new EnvWrapper(Env::Default())); env_guard->reset(new EnvWrapper(Env::Default()));
return env_guard->get(); return env_guard->get();
}); });
TEST_F(EnvRegistryTest, Basics) { TEST_F(ObjRegistryTest, Basics) {
std::string msg; std::string msg;
std::unique_ptr<Env> env_guard; std::unique_ptr<Env> env_guard;
auto registry = ObjectRegistry::NewInstance(); auto registry = ObjectRegistry::NewInstance();
@ -60,7 +61,7 @@ TEST_F(EnvRegistryTest, Basics) {
ASSERT_EQ(1, num_b); ASSERT_EQ(1, num_b);
} }
TEST_F(EnvRegistryTest, LocalRegistry) { TEST_F(ObjRegistryTest, LocalRegistry) {
std::string msg; std::string msg;
std::unique_ptr<Env> guard; std::unique_ptr<Env> guard;
auto registry = ObjectRegistry::NewInstance(); auto registry = ObjectRegistry::NewInstance();
@ -87,7 +88,7 @@ TEST_F(EnvRegistryTest, LocalRegistry) {
ASSERT_NE(registry->NewObject<Env>("test-global", &guard, &msg), nullptr); ASSERT_NE(registry->NewObject<Env>("test-global", &guard, &msg), nullptr);
} }
TEST_F(EnvRegistryTest, CheckShared) { TEST_F(ObjRegistryTest, CheckShared) {
std::shared_ptr<Env> shared; std::shared_ptr<Env> shared;
std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance(); std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance();
std::shared_ptr<ObjectLibrary> library = std::shared_ptr<ObjectLibrary> library =
@ -112,7 +113,7 @@ TEST_F(EnvRegistryTest, CheckShared) {
ASSERT_EQ(shared, nullptr); ASSERT_EQ(shared, nullptr);
} }
TEST_F(EnvRegistryTest, CheckStatic) { TEST_F(ObjRegistryTest, CheckStatic) {
Env* env = nullptr; Env* env = nullptr;
std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance(); std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance();
std::shared_ptr<ObjectLibrary> library = std::shared_ptr<ObjectLibrary> library =
@ -137,7 +138,7 @@ TEST_F(EnvRegistryTest, CheckStatic) {
ASSERT_NE(env, nullptr); ASSERT_NE(env, nullptr);
} }
TEST_F(EnvRegistryTest, CheckUnique) { TEST_F(ObjRegistryTest, CheckUnique) {
std::unique_ptr<Env> unique; std::unique_ptr<Env> unique;
std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance(); std::shared_ptr<ObjectRegistry> registry = ObjectRegistry::NewInstance();
std::shared_ptr<ObjectLibrary> library = std::shared_ptr<ObjectLibrary> library =
@ -162,7 +163,7 @@ TEST_F(EnvRegistryTest, CheckUnique) {
ASSERT_EQ(unique, nullptr); ASSERT_EQ(unique, nullptr);
} }
TEST_F(EnvRegistryTest, TestRegistryParents) { TEST_F(ObjRegistryTest, TestRegistryParents) {
auto grand = ObjectRegistry::Default(); auto grand = ObjectRegistry::Default();
auto parent = ObjectRegistry::NewInstance(); // parent with a grandparent auto parent = ObjectRegistry::NewInstance(); // parent with a grandparent
auto uncle = ObjectRegistry::NewInstance(grand); auto uncle = ObjectRegistry::NewInstance(grand);
@ -221,7 +222,7 @@ class MyCustomizable : public Customizable {
std::string name_; std::string name_;
}; };
TEST_F(EnvRegistryTest, TestManagedObjects) { TEST_F(ObjRegistryTest, TestManagedObjects) {
auto registry = ObjectRegistry::NewInstance(); auto registry = ObjectRegistry::NewInstance();
auto m_a1 = std::make_shared<MyCustomizable>("", "A"); auto m_a1 = std::make_shared<MyCustomizable>("", "A");
auto m_a2 = std::make_shared<MyCustomizable>("", "A"); auto m_a2 = std::make_shared<MyCustomizable>("", "A");
@ -238,7 +239,7 @@ TEST_F(EnvRegistryTest, TestManagedObjects) {
ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("A"), m_a2); ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("A"), m_a2);
} }
TEST_F(EnvRegistryTest, TestTwoManagedObjects) { TEST_F(ObjRegistryTest, TestTwoManagedObjects) {
auto registry = ObjectRegistry::NewInstance(); auto registry = ObjectRegistry::NewInstance();
auto m_a = std::make_shared<MyCustomizable>("", "A"); auto m_a = std::make_shared<MyCustomizable>("", "A");
auto m_b = std::make_shared<MyCustomizable>("", "B"); auto m_b = std::make_shared<MyCustomizable>("", "B");
@ -284,7 +285,7 @@ TEST_F(EnvRegistryTest, TestTwoManagedObjects) {
ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("B"), nullptr); ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("B"), nullptr);
} }
TEST_F(EnvRegistryTest, TestAlternateNames) { TEST_F(ObjRegistryTest, TestAlternateNames) {
auto registry = ObjectRegistry::NewInstance(); auto registry = ObjectRegistry::NewInstance();
auto m_a = std::make_shared<MyCustomizable>("", "A"); auto m_a = std::make_shared<MyCustomizable>("", "A");
auto m_b = std::make_shared<MyCustomizable>("", "B"); auto m_b = std::make_shared<MyCustomizable>("", "B");
@ -337,7 +338,7 @@ TEST_F(EnvRegistryTest, TestAlternateNames) {
ASSERT_EQ(objects.size(), 0U); ASSERT_EQ(objects.size(), 0U);
} }
TEST_F(EnvRegistryTest, TestTwoManagedClasses) { TEST_F(ObjRegistryTest, TestTwoManagedClasses) {
class MyCustomizable2 : public MyCustomizable { class MyCustomizable2 : public MyCustomizable {
public: public:
static const char* Type() { return "MyCustomizable2"; } static const char* Type() { return "MyCustomizable2"; }
@ -377,7 +378,7 @@ TEST_F(EnvRegistryTest, TestTwoManagedClasses) {
ASSERT_EQ(registry->GetManagedObject<MyCustomizable2>("A"), nullptr); ASSERT_EQ(registry->GetManagedObject<MyCustomizable2>("A"), nullptr);
} }
TEST_F(EnvRegistryTest, TestManagedObjectsWithParent) { TEST_F(ObjRegistryTest, TestManagedObjectsWithParent) {
auto base = ObjectRegistry::NewInstance(); auto base = ObjectRegistry::NewInstance();
auto registry = ObjectRegistry::NewInstance(base); auto registry = ObjectRegistry::NewInstance(base);
@ -397,10 +398,10 @@ TEST_F(EnvRegistryTest, TestManagedObjectsWithParent) {
ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("A"), m_b); ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("A"), m_b);
} }
TEST_F(EnvRegistryTest, TestGetOrCreateManagedObject) { TEST_F(ObjRegistryTest, TestGetOrCreateManagedObject) {
auto registry = ObjectRegistry::NewInstance(); auto registry = ObjectRegistry::NewInstance();
registry->AddLibrary("test")->Register<MyCustomizable>( registry->AddLibrary("test")->Register<MyCustomizable>(
"MC(@.*)?", ObjectLibrary::PatternEntry::AsIndividualId("MC"),
[](const std::string& uri, std::unique_ptr<MyCustomizable>* guard, [](const std::string& uri, std::unique_ptr<MyCustomizable>* guard,
std::string* /* errmsg */) { std::string* /* errmsg */) {
guard->reset(new MyCustomizable("MC", uri)); guard->reset(new MyCustomizable("MC", uri));
@ -411,14 +412,14 @@ TEST_F(EnvRegistryTest, TestGetOrCreateManagedObject) {
std::unordered_map<std::string, std::string> opt_map; std::unordered_map<std::string, std::string> opt_map;
ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("MC@A"), nullptr); ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("MC@A#1"), nullptr);
ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("MC@B"), nullptr); ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("MC@B#1"), nullptr);
ASSERT_OK(registry->GetOrCreateManagedObject("MC@A", &m_a)); ASSERT_OK(registry->GetOrCreateManagedObject("MC@A#1", &m_a));
ASSERT_OK(registry->GetOrCreateManagedObject("MC@B", &m_b)); ASSERT_OK(registry->GetOrCreateManagedObject("MC@B#1", &m_b));
ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("MC@A"), m_a); ASSERT_EQ(registry->GetManagedObject<MyCustomizable>("MC@A#1"), m_a);
ASSERT_OK(registry->GetOrCreateManagedObject("MC@A", &obj)); ASSERT_OK(registry->GetOrCreateManagedObject("MC@A#1", &obj));
ASSERT_EQ(obj, m_a); ASSERT_EQ(obj, m_a);
ASSERT_OK(registry->GetOrCreateManagedObject("MC@B", &obj)); ASSERT_OK(registry->GetOrCreateManagedObject("MC@B#1", &obj));
ASSERT_EQ(obj, m_b); ASSERT_EQ(obj, m_b);
ASSERT_OK(registry->ListManagedObjects(&objs)); ASSERT_OK(registry->ListManagedObjects(&objs));
ASSERT_EQ(objs.size(), 2U); ASSERT_EQ(objs.size(), 2U);
@ -426,11 +427,216 @@ TEST_F(EnvRegistryTest, TestGetOrCreateManagedObject) {
objs.clear(); objs.clear();
m_a.reset(); m_a.reset();
obj.reset(); obj.reset();
ASSERT_OK(registry->GetOrCreateManagedObject("MC@A", &m_a)); ASSERT_OK(registry->GetOrCreateManagedObject("MC@A#1", &m_a));
ASSERT_EQ(1, m_a.use_count()); ASSERT_EQ(1, m_a.use_count());
ASSERT_OK(registry->GetOrCreateManagedObject("MC@B", &obj)); ASSERT_OK(registry->GetOrCreateManagedObject("MC@B#1", &obj));
ASSERT_EQ(2, obj.use_count()); ASSERT_EQ(2, obj.use_count());
} }
class PatternEntryTest : public testing::Test {};
TEST_F(PatternEntryTest, TestSimpleEntry) {
ObjectLibrary::PatternEntry entry("ABC", true);
ASSERT_TRUE(entry.Matches("ABC"));
ASSERT_FALSE(entry.Matches("AABC"));
ASSERT_FALSE(entry.Matches("ABCA"));
ASSERT_FALSE(entry.Matches("AABCA"));
ASSERT_FALSE(entry.Matches("AB"));
ASSERT_FALSE(entry.Matches("BC"));
ASSERT_FALSE(entry.Matches("ABD"));
ASSERT_FALSE(entry.Matches("BCA"));
}
TEST_F(PatternEntryTest, TestPatternEntry) {
// Matches A:+
ObjectLibrary::PatternEntry entry("A", false);
entry.AddSeparator(":");
ASSERT_FALSE(entry.Matches("A"));
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("AB"));
ASSERT_FALSE(entry.Matches("B"));
ASSERT_FALSE(entry.Matches("A:"));
ASSERT_FALSE(entry.Matches("AA:"));
ASSERT_FALSE(entry.Matches("AA:B"));
ASSERT_FALSE(entry.Matches("AA:BB"));
ASSERT_TRUE(entry.Matches("A:B"));
ASSERT_TRUE(entry.Matches("A:BB"));
entry.SetOptional(true); // Now matches "A" or "A:+"
ASSERT_TRUE(entry.Matches("A"));
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("AB"));
ASSERT_FALSE(entry.Matches("B"));
ASSERT_FALSE(entry.Matches("A:"));
ASSERT_FALSE(entry.Matches("AA:"));
ASSERT_FALSE(entry.Matches("AA:B"));
ASSERT_FALSE(entry.Matches("AA:BB"));
ASSERT_TRUE(entry.Matches("A:B"));
ASSERT_TRUE(entry.Matches("A:BB"));
}
TEST_F(PatternEntryTest, TestSuffixEntry) {
ObjectLibrary::PatternEntry entry("AA", true);
entry.AddSuffix("BB");
ASSERT_TRUE(entry.Matches("AA"));
ASSERT_TRUE(entry.Matches("AABB"));
ASSERT_FALSE(entry.Matches("A"));
ASSERT_FALSE(entry.Matches("AB"));
ASSERT_FALSE(entry.Matches("B"));
ASSERT_FALSE(entry.Matches("BB"));
ASSERT_FALSE(entry.Matches("ABA"));
ASSERT_FALSE(entry.Matches("BBAA"));
ASSERT_FALSE(entry.Matches("AABBA"));
ASSERT_FALSE(entry.Matches("AABBB"));
}
TEST_F(PatternEntryTest, TestNumericEntry) {
ObjectLibrary::PatternEntry entry("A", false);
entry.AddNumber(":");
ASSERT_FALSE(entry.Matches("A"));
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("A:"));
ASSERT_FALSE(entry.Matches("AA:"));
ASSERT_TRUE(entry.Matches("A:1"));
ASSERT_TRUE(entry.Matches("A:11"));
ASSERT_FALSE(entry.Matches("AA:1"));
ASSERT_FALSE(entry.Matches("AA:11"));
ASSERT_FALSE(entry.Matches("A:B"));
ASSERT_FALSE(entry.Matches("A:1B"));
ASSERT_FALSE(entry.Matches("A:B1"));
}
TEST_F(PatternEntryTest, TestIndividualIdEntry) {
auto entry = ObjectLibrary::PatternEntry::AsIndividualId("AA");
ASSERT_TRUE(entry.Matches("AA"));
ASSERT_TRUE(entry.Matches("AA@123#456"));
ASSERT_TRUE(entry.Matches("AA@deadbeef#id"));
ASSERT_FALSE(entry.Matches("A"));
ASSERT_FALSE(entry.Matches("AAA"));
ASSERT_FALSE(entry.Matches("AA@123"));
ASSERT_FALSE(entry.Matches("AA@123#"));
ASSERT_FALSE(entry.Matches("AA@#123"));
}
TEST_F(PatternEntryTest, TestTwoNameEntry) {
ObjectLibrary::PatternEntry entry("A");
entry.AnotherName("B");
ASSERT_TRUE(entry.Matches("A"));
ASSERT_TRUE(entry.Matches("B"));
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("BB"));
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("BA"));
ASSERT_FALSE(entry.Matches("AB"));
}
TEST_F(PatternEntryTest, TestTwoPatternEntry) {
ObjectLibrary::PatternEntry entry("AA", false);
entry.AddSeparator(":");
entry.AddSeparator(":");
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("AA:"));
ASSERT_FALSE(entry.Matches("AA::"));
ASSERT_FALSE(entry.Matches("AA::12"));
ASSERT_TRUE(entry.Matches("AA:1:2"));
ASSERT_TRUE(entry.Matches("AA:1:2:"));
ObjectLibrary::PatternEntry entry2("AA", false);
entry2.AddSeparator("::");
entry2.AddSeparator("##");
ASSERT_FALSE(entry2.Matches("AA"));
ASSERT_FALSE(entry2.Matches("AA:"));
ASSERT_FALSE(entry2.Matches("AA::"));
ASSERT_FALSE(entry2.Matches("AA::#"));
ASSERT_FALSE(entry2.Matches("AA::##"));
ASSERT_FALSE(entry2.Matches("AA##1::2"));
ASSERT_FALSE(entry2.Matches("AA::123##"));
ASSERT_TRUE(entry2.Matches("AA::1##2"));
ASSERT_TRUE(entry2.Matches("AA::12##34:"));
ASSERT_TRUE(entry2.Matches("AA::12::34##56"));
ASSERT_TRUE(entry2.Matches("AA::12##34::56"));
}
TEST_F(PatternEntryTest, TestTwoNumbersEntry) {
ObjectLibrary::PatternEntry entry("AA", false);
entry.AddNumber(":");
entry.AddNumber(":");
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("AA:"));
ASSERT_FALSE(entry.Matches("AA::"));
ASSERT_FALSE(entry.Matches("AA::12"));
ASSERT_FALSE(entry.Matches("AA:1:2:"));
ASSERT_TRUE(entry.Matches("AA:1:2"));
ASSERT_TRUE(entry.Matches("AA:12:23456"));
ObjectLibrary::PatternEntry entry2("AA", false);
entry2.AddNumber(":");
entry2.AddNumber("#");
ASSERT_FALSE(entry2.Matches("AA"));
ASSERT_FALSE(entry2.Matches("AA:"));
ASSERT_FALSE(entry2.Matches("AA:#"));
ASSERT_FALSE(entry2.Matches("AA#:"));
ASSERT_FALSE(entry2.Matches("AA:123#"));
ASSERT_FALSE(entry2.Matches("AA:123#B"));
ASSERT_FALSE(entry2.Matches("AA:B#123"));
ASSERT_TRUE(entry2.Matches("AA:1#2"));
ASSERT_FALSE(entry2.Matches("AA:123#23:"));
ASSERT_FALSE(entry2.Matches("AA::12#234"));
}
TEST_F(PatternEntryTest, TestPatternAndSuffix) {
ObjectLibrary::PatternEntry entry("AA", false);
entry.AddSeparator("::");
entry.AddSuffix("##");
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("AA::"));
ASSERT_FALSE(entry.Matches("AA::##"));
ASSERT_FALSE(entry.Matches("AB::1##"));
ASSERT_FALSE(entry.Matches("AB::1##2"));
ASSERT_FALSE(entry.Matches("AA##1::"));
ASSERT_TRUE(entry.Matches("AA::1##"));
ASSERT_FALSE(entry.Matches("AA::1###"));
ObjectLibrary::PatternEntry entry2("AA", false);
entry2.AddSuffix("::");
entry2.AddSeparator("##");
ASSERT_FALSE(entry2.Matches("AA"));
ASSERT_FALSE(entry2.Matches("AA::"));
ASSERT_FALSE(entry2.Matches("AA::##"));
ASSERT_FALSE(entry2.Matches("AB::1##"));
ASSERT_FALSE(entry2.Matches("AB::1##2"));
ASSERT_TRUE(entry2.Matches("AA::##12"));
}
TEST_F(PatternEntryTest, TestTwoNamesAndPattern) {
ObjectLibrary::PatternEntry entry("AA", true);
entry.AddSeparator("::");
entry.AnotherName("BBB");
ASSERT_TRUE(entry.Matches("AA"));
ASSERT_TRUE(entry.Matches("AA::1"));
ASSERT_TRUE(entry.Matches("BBB"));
ASSERT_TRUE(entry.Matches("BBB::2"));
ASSERT_FALSE(entry.Matches("AA::"));
ASSERT_FALSE(entry.Matches("AAA::"));
ASSERT_FALSE(entry.Matches("BBB::"));
entry.SetOptional(false);
ASSERT_FALSE(entry.Matches("AA"));
ASSERT_FALSE(entry.Matches("BBB"));
ASSERT_FALSE(entry.Matches("AA::"));
ASSERT_FALSE(entry.Matches("AAA::"));
ASSERT_FALSE(entry.Matches("BBB::"));
ASSERT_TRUE(entry.Matches("AA::1"));
ASSERT_TRUE(entry.Matches("BBB::2"));
}
} // namespace ROCKSDB_NAMESPACE } // namespace ROCKSDB_NAMESPACE
int main(int argc, char** argv) { int main(int argc, char** argv) {
@ -442,7 +648,7 @@ int main(int argc, char** argv) {
#include <stdio.h> #include <stdio.h>
int main(int /*argc*/, char** /*argv*/) { int main(int /*argc*/, char** /*argv*/) {
fprintf(stderr, "SKIPPED as EnvRegistry is not supported in ROCKSDB_LITE\n"); fprintf(stderr, "SKIPPED as ObjRegistry is not supported in ROCKSDB_LITE\n");
return 0; return 0;
} }