fork of https://github.com/rust-rocksdb/rust-rocksdb for nextgraph
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
583 lines
22 KiB
583 lines
22 KiB
// Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
|
|
// This source code is licensed under both the GPLv2 (found in the
|
|
// COPYING file in the root directory) and Apache 2.0 License
|
|
// (found in the LICENSE.Apache file in the root directory).
|
|
|
|
#pragma once
|
|
|
|
|
|
#include <functional>
|
|
#include <map>
|
|
#include <memory>
|
|
#include <mutex>
|
|
#include <string>
|
|
#include <unordered_map>
|
|
#include <unordered_set>
|
|
#include <vector>
|
|
|
|
#include "rocksdb/status.h"
|
|
|
|
namespace ROCKSDB_NAMESPACE {
|
|
class Customizable;
|
|
class Logger;
|
|
class ObjectLibrary;
|
|
|
|
// Returns a new T when called with a string. Populates the std::unique_ptr
|
|
// argument if granting ownership to caller.
|
|
template <typename T>
|
|
using FactoryFunc =
|
|
std::function<T*(const std::string&, std::unique_ptr<T>*, std::string*)>;
|
|
|
|
// The signature of the function for loading factories
|
|
// into an object library. This method is expected to register
|
|
// factory functions in the supplied ObjectLibrary.
|
|
// The ObjectLibrary is the library in which the factories will be loaded.
|
|
// The std::string is the argument passed to the loader function.
|
|
// The RegistrarFunc should return the number of objects loaded into this
|
|
// library
|
|
using RegistrarFunc = std::function<int(ObjectLibrary&, const std::string&)>;
|
|
|
|
template <typename T>
|
|
using ConfigureFunc = std::function<Status(T*)>;
|
|
|
|
class ObjectLibrary {
|
|
private:
|
|
// Base class for an Entry in the Registry.
|
|
class Entry {
|
|
public:
|
|
virtual ~Entry() {}
|
|
virtual bool Matches(const std::string& target) const = 0;
|
|
virtual const char* Name() const = 0;
|
|
};
|
|
|
|
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).+ or 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 : public Entry {
|
|
private:
|
|
enum Quantifier {
|
|
kMatchZeroOrMore, // [suffix].*
|
|
kMatchAtLeastOne, // [suffix].+
|
|
kMatchExact, // [suffix]
|
|
kMatchInteger, // [suffix][0-9]+
|
|
kMatchDecimal, // [suffix][0-9]+[.][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 PatternEntry 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
|
|
// If at_least_one is true, the separator must be followed by at least
|
|
// one character (e.g. separator.+).
|
|
// If at_least_one is false, the separator may be followed by zero or
|
|
// more characters (e.g. separator.*).
|
|
PatternEntry& AddSeparator(const std::string& separator,
|
|
bool at_least_one = true) {
|
|
slength_ += separator.size();
|
|
if (at_least_one) {
|
|
separators_.emplace_back(separator, kMatchAtLeastOne);
|
|
++slength_;
|
|
} else {
|
|
separators_.emplace_back(separator, kMatchZeroOrMore);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
// Adds a separator (exact match of separator with trailing numbers) to the
|
|
// entry
|
|
PatternEntry& AddNumber(const std::string& separator, bool is_int = true) {
|
|
separators_.emplace_back(separator,
|
|
(is_int) ? kMatchInteger : kMatchDecimal);
|
|
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 override;
|
|
const char* Name() const override { 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:
|
|
// An Entry containing a FactoryFunc for creating new Objects
|
|
template <typename T>
|
|
class FactoryEntry : public Entry {
|
|
public:
|
|
FactoryEntry(Entry* e, FactoryFunc<T> f)
|
|
: entry_(e), factory_(std::move(f)) {}
|
|
bool Matches(const std::string& target) const override {
|
|
return entry_->Matches(target);
|
|
}
|
|
const char* Name() const override { return entry_->Name(); }
|
|
|
|
// Creates a new T object.
|
|
T* NewFactoryObject(const std::string& target, std::unique_ptr<T>* guard,
|
|
std::string* msg) const {
|
|
return factory_(target, guard, msg);
|
|
}
|
|
const FactoryFunc<T>& GetFactory() const { return factory_; }
|
|
|
|
private:
|
|
std::unique_ptr<Entry> entry_; // What to match for this entry
|
|
FactoryFunc<T> factory_;
|
|
}; // End class FactoryEntry
|
|
public:
|
|
explicit ObjectLibrary(const std::string& id) { id_ = id; }
|
|
|
|
const std::string& GetID() const { return id_; }
|
|
|
|
// Finds the factory function for the input target.
|
|
// @see PatternEntry for the matching rules to target
|
|
// @return If matched, the FactoryFunc for this target, else nullptr
|
|
template <typename T>
|
|
FactoryFunc<T> FindFactory(const std::string& target) 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(target)) {
|
|
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.
|
|
// This method returns the sum of all factories registered for all types.
|
|
// @param num_types returns how many unique types are registered.
|
|
size_t GetFactoryCount(size_t* num_types) const;
|
|
|
|
// Returns the number of factories registered for this library
|
|
// for the input type.
|
|
// @param num_types returns how many unique types are registered.
|
|
size_t GetFactoryCount(const std::string& type) const;
|
|
|
|
// Returns the registered factory names for the input type
|
|
// names is updated to include the names for the type
|
|
void GetFactoryNames(const std::string& type,
|
|
std::vector<std::string>* names) const;
|
|
|
|
void GetFactoryTypes(std::unordered_set<std::string>* types) const;
|
|
|
|
void Dump(Logger* logger) const;
|
|
|
|
// Registers the factory with the library for the name.
|
|
// If name==target, the factory may be used to create a new object.
|
|
template <typename T>
|
|
const FactoryFunc<T>& AddFactory(const std::string& name,
|
|
const FactoryFunc<T>& func) {
|
|
std::unique_ptr<Entry> entry(
|
|
new FactoryEntry<T>(new PatternEntry(name), func));
|
|
AddFactoryEntry(T::Type(), std::move(entry));
|
|
return func;
|
|
}
|
|
|
|
// Registers the factory with the library for the entry.
|
|
// If the entry matches the target, the factory may be used to create a new
|
|
// object.
|
|
// @see PatternEntry for the matching rules.
|
|
// NOTE: This function replaces the old ObjectLibrary::Register()
|
|
template <typename T>
|
|
const FactoryFunc<T>& AddFactory(const PatternEntry& entry,
|
|
const FactoryFunc<T>& func) {
|
|
std::unique_ptr<Entry> factory(
|
|
new FactoryEntry<T>(new PatternEntry(entry), func));
|
|
AddFactoryEntry(T::Type(), std::move(factory));
|
|
return func;
|
|
}
|
|
|
|
// Invokes the registrar function with the supplied arg for this library.
|
|
int Register(const RegistrarFunc& registrar, const std::string& arg) {
|
|
return registrar(*this, arg);
|
|
}
|
|
|
|
// Returns the default ObjectLibrary
|
|
static std::shared_ptr<ObjectLibrary>& Default();
|
|
|
|
private:
|
|
void AddFactoryEntry(const char* type, std::unique_ptr<Entry>&& entry) {
|
|
std::unique_lock<std::mutex> lock(mu_);
|
|
auto& factories = factories_[type];
|
|
factories.emplace_back(std::move(entry));
|
|
}
|
|
|
|
// Protects the entry map
|
|
mutable std::mutex mu_;
|
|
// ** FactoryFunctions for this loader, organized by type
|
|
std::unordered_map<std::string, std::vector<std::unique_ptr<Entry>>>
|
|
factories_;
|
|
|
|
// The name for this library
|
|
std::string id_;
|
|
};
|
|
|
|
// The ObjectRegistry is used to register objects that can be created by a
|
|
// name/pattern at run-time where the specific implementation of the object may
|
|
// not be known in advance.
|
|
class ObjectRegistry {
|
|
public:
|
|
static std::shared_ptr<ObjectRegistry> NewInstance();
|
|
static std::shared_ptr<ObjectRegistry> NewInstance(
|
|
const std::shared_ptr<ObjectRegistry>& parent);
|
|
static std::shared_ptr<ObjectRegistry> Default();
|
|
explicit ObjectRegistry(const std::shared_ptr<ObjectRegistry>& parent)
|
|
: parent_(parent) {}
|
|
explicit ObjectRegistry(const std::shared_ptr<ObjectLibrary>& library);
|
|
|
|
std::shared_ptr<ObjectLibrary> AddLibrary(const std::string& id) {
|
|
auto library = std::make_shared<ObjectLibrary>(id);
|
|
AddLibrary(library);
|
|
return library;
|
|
}
|
|
|
|
void AddLibrary(const std::shared_ptr<ObjectLibrary>& library) {
|
|
std::unique_lock<std::mutex> lock(library_mutex_);
|
|
libraries_.push_back(library);
|
|
}
|
|
|
|
void AddLibrary(const std::string& id, const RegistrarFunc& registrar,
|
|
const std::string& arg) {
|
|
auto library = AddLibrary(id);
|
|
library->Register(registrar, arg);
|
|
}
|
|
|
|
// Finds the factory for target and instantiates a new T.
|
|
// Returns NotSupported if no factory is found
|
|
// Returns InvalidArgument if a factory is found but the factory failed.
|
|
template <typename T>
|
|
Status NewObject(const std::string& target, T** object,
|
|
std::unique_ptr<T>* guard) {
|
|
assert(guard != nullptr);
|
|
guard->reset();
|
|
auto factory = FindFactory<T>(target);
|
|
if (factory != nullptr) {
|
|
std::string errmsg;
|
|
*object = factory(target, guard, &errmsg);
|
|
if (*object != nullptr) {
|
|
return Status::OK();
|
|
} else if (errmsg.empty()) {
|
|
return Status::InvalidArgument(
|
|
std::string("Could not load ") + T::Type(), target);
|
|
} else {
|
|
return Status::InvalidArgument(errmsg, target);
|
|
}
|
|
} else {
|
|
return Status::NotSupported(std::string("Could not load ") + T::Type(),
|
|
target);
|
|
}
|
|
}
|
|
// Creates a new unique T using the input factory functions.
|
|
// Returns OK if a new unique T was successfully created
|
|
// Returns NotSupported if the type/target could not be created
|
|
// Returns InvalidArgument if the factory return an unguarded object
|
|
// (meaning it cannot be managed by a unique ptr)
|
|
template <typename T>
|
|
Status NewUniqueObject(const std::string& target,
|
|
std::unique_ptr<T>* result) {
|
|
T* ptr = nullptr;
|
|
std::unique_ptr<T> guard;
|
|
Status s = NewObject(target, &ptr, &guard);
|
|
if (!s.ok()) {
|
|
return s;
|
|
} else if (guard) {
|
|
result->reset(guard.release());
|
|
return Status::OK();
|
|
} else {
|
|
return Status::InvalidArgument(std::string("Cannot make a unique ") +
|
|
T::Type() + " from unguarded one ",
|
|
target);
|
|
}
|
|
}
|
|
|
|
// Creates a new shared T using the input factory functions.
|
|
// Returns OK if a new shared T was successfully created
|
|
// Returns NotSupported if the type/target could not be created
|
|
// Returns InvalidArgument if the factory return an unguarded object
|
|
// (meaning it cannot be managed by a shared ptr)
|
|
template <typename T>
|
|
Status NewSharedObject(const std::string& target,
|
|
std::shared_ptr<T>* result) {
|
|
std::unique_ptr<T> guard;
|
|
T* ptr = nullptr;
|
|
Status s = NewObject(target, &ptr, &guard);
|
|
if (!s.ok()) {
|
|
return s;
|
|
} else if (guard) {
|
|
result->reset(guard.release());
|
|
return Status::OK();
|
|
} else {
|
|
return Status::InvalidArgument(std::string("Cannot make a shared ") +
|
|
T::Type() + " from unguarded one ",
|
|
target);
|
|
}
|
|
}
|
|
|
|
// Creates a new static T using the input factory functions.
|
|
// Returns OK if a new static T was successfully created
|
|
// Returns NotSupported if the type/target could not be created
|
|
// Returns InvalidArgument if the factory return a guarded object
|
|
// (meaning it is managed by a unique ptr)
|
|
template <typename T>
|
|
Status NewStaticObject(const std::string& target, T** result) {
|
|
std::unique_ptr<T> guard;
|
|
T* ptr = nullptr;
|
|
Status s = NewObject(target, &ptr, &guard);
|
|
if (!s.ok()) {
|
|
return s;
|
|
} else if (guard.get()) {
|
|
return Status::InvalidArgument(std::string("Cannot make a static ") +
|
|
T::Type() + " from a guarded one ",
|
|
target);
|
|
} else {
|
|
*result = ptr;
|
|
return Status::OK();
|
|
}
|
|
}
|
|
|
|
// Sets the object for the given id/type to be the input object
|
|
// If the registry does not contain this id/type, the object is added and OK
|
|
// is returned. If the registry contains a different object, an error is
|
|
// returned. If the registry contains the input object, OK is returned.
|
|
template <typename T>
|
|
Status SetManagedObject(const std::shared_ptr<T>& object) {
|
|
assert(object != nullptr);
|
|
return SetManagedObject(object->GetId(), object);
|
|
}
|
|
|
|
template <typename T>
|
|
Status SetManagedObject(const std::string& id,
|
|
const std::shared_ptr<T>& object) {
|
|
const auto c = std::static_pointer_cast<Customizable>(object);
|
|
return SetManagedObject(T::Type(), id, c);
|
|
}
|
|
|
|
// Returns the object for the given id, if one exists.
|
|
// If the object is not found in the registry, a nullptr is returned
|
|
template <typename T>
|
|
std::shared_ptr<T> GetManagedObject(const std::string& id) const {
|
|
auto c = GetManagedObject(T::Type(), id);
|
|
return std::static_pointer_cast<T>(c);
|
|
}
|
|
|
|
// Returns the set of managed objects found in the registry matching
|
|
// the input type and ID.
|
|
// If the input id is not empty, then only objects of that class
|
|
// (IsInstanceOf(id)) will be returned (for example, only return LRUCache
|
|
// objects) If the input id is empty, then all objects of that type (all Cache
|
|
// objects)
|
|
template <typename T>
|
|
Status ListManagedObjects(const std::string& id,
|
|
std::vector<std::shared_ptr<T>>* results) const {
|
|
std::vector<std::shared_ptr<Customizable>> customizables;
|
|
results->clear();
|
|
Status s = ListManagedObjects(T::Type(), id, &customizables);
|
|
if (s.ok()) {
|
|
for (const auto& c : customizables) {
|
|
results->push_back(std::static_pointer_cast<T>(c));
|
|
}
|
|
}
|
|
return s;
|
|
}
|
|
|
|
template <typename T>
|
|
Status ListManagedObjects(std::vector<std::shared_ptr<T>>* results) const {
|
|
return ListManagedObjects("", results);
|
|
}
|
|
|
|
// Creates a new ManagedObject in the registry for the id if one does not
|
|
// currently exist. If an object with that ID already exists, the current
|
|
// object is returned.
|
|
//
|
|
// The ID is the identifier of the object to be returned/created and returned
|
|
// in result
|
|
// If a new object is created (using the object factories), the cfunc
|
|
// parameter will be invoked to configure the new object.
|
|
template <typename T>
|
|
Status GetOrCreateManagedObject(const std::string& id,
|
|
std::shared_ptr<T>* result,
|
|
const ConfigureFunc<T>& cfunc = nullptr) {
|
|
if (parent_ != nullptr) {
|
|
auto object = parent_->GetManagedObject(T::Type(), id);
|
|
if (object != nullptr) {
|
|
*result = std::static_pointer_cast<T>(object);
|
|
return Status::OK();
|
|
}
|
|
}
|
|
{
|
|
std::unique_lock<std::mutex> lock(objects_mutex_);
|
|
auto key = ToManagedObjectKey(T::Type(), id);
|
|
auto iter = managed_objects_.find(key);
|
|
if (iter != managed_objects_.end()) {
|
|
auto object = iter->second.lock();
|
|
if (object != nullptr) {
|
|
*result = std::static_pointer_cast<T>(object);
|
|
return Status::OK();
|
|
}
|
|
}
|
|
std::shared_ptr<T> object;
|
|
Status s = NewSharedObject(id, &object);
|
|
if (s.ok() && cfunc != nullptr) {
|
|
s = cfunc(object.get());
|
|
}
|
|
if (s.ok()) {
|
|
auto c = std::static_pointer_cast<Customizable>(object);
|
|
if (id != c->Name()) {
|
|
// If the ID is not the base name of the class, add the new
|
|
// object under the input ID
|
|
managed_objects_[key] = c;
|
|
}
|
|
if (id != c->GetId() && c->GetId() != c->Name()) {
|
|
// If the input and current ID do not match, and the
|
|
// current ID is not the base bame, add the new object under
|
|
// its new ID
|
|
key = ToManagedObjectKey(T::Type(), c->GetId());
|
|
managed_objects_[key] = c;
|
|
}
|
|
*result = object;
|
|
}
|
|
return s;
|
|
}
|
|
}
|
|
|
|
// Returns the number of factories registered for this library
|
|
// for the input type.
|
|
// @param num_types returns how many unique types are registered.
|
|
size_t GetFactoryCount(const std::string& type) const;
|
|
|
|
// Returns the names of registered factories for the input type.
|
|
// names is updated to include the names for the type
|
|
void GetFactoryNames(const std::string& type,
|
|
std::vector<std::string>* names) const;
|
|
|
|
void GetFactoryTypes(std::unordered_set<std::string>* types) const;
|
|
|
|
// Dump the contents of the registry to the logger
|
|
void Dump(Logger* logger) const;
|
|
|
|
// Invokes the input function to retrieve the properties for this plugin.
|
|
int RegisterPlugin(const std::string& name, const RegistrarFunc& func);
|
|
|
|
private:
|
|
static std::string ToManagedObjectKey(const std::string& type,
|
|
const std::string& id) {
|
|
return type + "://" + id;
|
|
}
|
|
|
|
// Returns the Customizable managed object associated with the key (Type/ID).
|
|
// If not found, nullptr is returned.
|
|
std::shared_ptr<Customizable> GetManagedObject(const std::string& type,
|
|
const std::string& id) const;
|
|
Status ListManagedObjects(
|
|
const std::string& type, const std::string& pattern,
|
|
std::vector<std::shared_ptr<Customizable>>* results) const;
|
|
// Sets the managed object associated with the key (Type/ID) to c.
|
|
// If the named managed object does not exist, the object is added and OK is
|
|
// returned If the object exists and is the same as c, OK is returned
|
|
// Otherwise, an error status is returned.
|
|
Status SetManagedObject(const std::string& type, const std::string& id,
|
|
const std::shared_ptr<Customizable>& c);
|
|
|
|
// Searches (from back to front) the libraries looking for the
|
|
// factory that matches this name.
|
|
// 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 nullptr;
|
|
} else {
|
|
return parent_->FindFactory<T>(name);
|
|
}
|
|
}
|
|
|
|
// The set of libraries to search for factories for this registry.
|
|
// The libraries are searched in reverse order (back to front) when
|
|
// searching for entries.
|
|
std::vector<std::shared_ptr<ObjectLibrary>> libraries_;
|
|
std::vector<std::string> plugins_;
|
|
static std::unordered_map<std::string, RegistrarFunc> builtins_;
|
|
std::map<std::string, std::weak_ptr<Customizable>> managed_objects_;
|
|
std::shared_ptr<ObjectRegistry> parent_;
|
|
mutable std::mutex objects_mutex_; // Mutex for managed objects
|
|
mutable std::mutex library_mutex_; // Mutex for managed libraries
|
|
};
|
|
} // namespace ROCKSDB_NAMESPACE
|
|
|