// Copyright (c) 2011-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). // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. See the AUTHORS file for names of contributors. #pragma once #include "rocksdb/configurable.h" #include "rocksdb/status.h" namespace ROCKSDB_NAMESPACE { /** * Customizable a base class used by the rocksdb that describes a * standard way of configuring and creating objects. Customizable objects * are configurable objects that can be created from an ObjectRegistry. * * Customizable classes are used when there are multiple potential * implementations of a class for use by RocksDB (e.g. Table, Cache, * MergeOperator, etc). The abstract base class is expected to define a method * declaring its type and a factory method for creating one of these, such as: * static const char *Type() { return "Table"; } * static Status CreateFromString(const ConfigOptions& options, * const std::string& id, * std::shared_ptr* result); * The "Type" string is expected to be unique (no two base classes are the same * type). This factory is expected, based on the options and id, create and * return the appropriate derived type of the customizable class (e.g. * BlockBasedTableFactory, PlainTableFactory, etc). For extension developers, * helper classes and methods are provided for writing this factory. * * Instances of a Customizable class need to define: * - A "static const char *kClassName()" method. This method defines the name * of the class instance (e.g. BlockBasedTable, LRUCache) and is used by the * CheckedCast method. * - The Name() of the object. This name is used when creating and saving * instances of this class. Typically this name will be the same as * kClassName(). * * Additionally, Customizable classes should register any options used to * configure themselves with the Configurable subsystem. * * When a Customizable is being created, the "name" property specifies * the name of the instance being created. * For custom objects, their configuration and name can be specified by: * [prop]={name=X;option 1 = value1[; option2=value2...]} * * [prop].name=X * [prop].option1 = value1 * * [prop].name=X * X.option1 =value1 */ class Customizable : public Configurable { public: ~Customizable() override {} // Returns the name of this class of Customizable virtual const char* Name() const = 0; // Returns an identifier for this Customizable. // This could be its name or something more complex (like its URL/pattern). // Used for pretty printing. virtual std::string GetId() const { std::string id = Name(); return id; } // This is typically determined by if the input name matches the // name of this object. // This method is typically used in conjunction with CheckedCast to find the // derived class instance from its base. For example, if you have an Env // and want the "Default" env, you would IsInstanceOf("Default") to get // the default implementation. This method should be used when you need a // specific derivative or implementation of a class. // // Intermediary caches (such as SharedCache) may wish to override this method // to check for the intermediary name (SharedCache). Classes with multiple // potential names (e.g. "PosixEnv", "DefaultEnv") may also wish to override // this method. // // Note that IsInstanceOf only uses the "is-a" relationship and not "has-a". // Wrapped classes that have an Inner "has-a" should not be returned. // // @param name The name of the instance to find. // Returns true if the class is an instance of the input name. virtual bool IsInstanceOf(const std::string& name) const { if (name.empty()) { return false; } else if (name == Name()) { return true; } else { const char* nickname = NickName(); if (nickname != nullptr && name == nickname) { return true; } else { return false; } } } const void* GetOptionsPtr(const std::string& name) const override { const void* ptr = Configurable::GetOptionsPtr(name); if (ptr != nullptr) { return ptr; } else { const auto inner = Inner(); if (inner != nullptr) { return inner->GetOptionsPtr(name); } else { return nullptr; } } } // Returns the named instance of the Customizable as a T*, or nullptr if not // found. This method uses IsInstanceOf/Inner to find the appropriate class // instance and then casts it to the expected return type. template const T* CheckedCast() const { if (IsInstanceOf(T::kClassName())) { return static_cast(this); } else { const auto inner = Inner(); if (inner != nullptr) { return inner->CheckedCast(); } else { return nullptr; } } } template T* CheckedCast() { if (IsInstanceOf(T::kClassName())) { return static_cast(this); } else { auto inner = const_cast(Inner()); if (inner != nullptr) { return inner->CheckedCast(); } else { return nullptr; } } } // Checks to see if this Customizable is equivalent to other. // This method assumes that the two objects are of the same class. // @param config_options Controls how the options are compared. // @param other The other object to compare to. // @param mismatch If the objects do not match, this parameter contains // the name of the option that triggered the match failure. // @param True if the objects match, false otherwise. // @see Configurable::AreEquivalent for more details bool AreEquivalent(const ConfigOptions& config_options, const Configurable* other, std::string* mismatch) const override; #ifndef ROCKSDB_LITE // Gets the value of the option associated with the input name // @see Configurable::GetOption for more details Status GetOption(const ConfigOptions& config_options, const std::string& name, std::string* value) const override; #endif // ROCKSDB_LITE // Helper method for getting for parsing the opt_value into the corresponding // options for use in potentially creating a new Customizable object (this // method is primarily a support method for LoadSharedObject et al for new // Customizable objects). The opt_value may be either name-value pairs // separated by ";" (a=b; c=d), or a simple name (a). In order to create a new // Customizable, the ID is determined by: // - If the value is a simple name (e.g. "BlockBasedTable"), the id is this // name; // - Otherwise, if there is a "id=value", the id is set to "value" // - Otherwise, if the input customizable is not null, custom->GetId is used // - Otherwise, an error is returned. // // If the opt_value is name-value pairs, these pairs will be returned in // options (without the id pair). If the ID being returned matches the ID of // the input custom object, then the options from the input object will also // be added to the returned options. // // This method returns non-OK if the ID could not be found, or if the // opt_value could not be parsed into name-value pairs. static Status GetOptionsMap( const ConfigOptions& config_options, const Customizable* custom, const std::string& opt_value, std::string* id, std::unordered_map* options); // Helper method to configure a new object with the supplied options. // If the object is not null and invoke_prepare_options=true, the object // will be configured and prepared. // Returns success if the object is properly configured and (optionally) // prepared Returns InvalidArgument if the object is nullptr and there are // options in the map Returns the result of the ConfigureFromMap or // PrepareOptions static Status ConfigureNewObject( const ConfigOptions& config_options, Customizable* object, const std::unordered_map& options); // Returns the inner class when a Customizable implements a has-a (wrapped) // relationship. Derived classes that implement a has-a must override this // method in order to get CheckedCast to function properly. virtual const Customizable* Inner() const { return nullptr; } protected: // Generates a ID specific for this instance of the customizable. // The unique ID is of the form :#pid, where: // - name is the Name() of this object; // - addr is the memory address of this object; // - pid is the process ID of this process ID for this process. // Note that if obj1 and obj2 have the same unique IDs, they must be the // same. However, if an object is deleted and recreated, it may have the // same unique ID as a predecessor // // This method is useful for objects (especially ManagedObjects) that // wish to generate an ID that is specific for this instance and wish to // override the GetId() method. std::string GenerateIndividualId() const; // Some classes have both a class name (e.g. PutOperator) and a nickname // (e.g. put). Classes can override this method to return a // nickname. Nicknames can be used by InstanceOf and object creation. virtual const char* NickName() const { return ""; } // Given a name (e.g. rocksdb.my.type.opt), returns the short name (opt) std::string GetOptionName(const std::string& long_name) const override; #ifndef ROCKSDB_LITE std::string SerializeOptions(const ConfigOptions& options, const std::string& prefix) const override; #endif // ROCKSDB_LITE }; } // namespace ROCKSDB_NAMESPACE