#ifndef XRPL_PROTOCOL_KNOWNFORMATS_H_INCLUDED #define XRPL_PROTOCOL_KNOWNFORMATS_H_INCLUDED #include #include #include #include #include #include namespace ripple { /** Manages a list of known formats. Each format has a name, an associated KeyType (typically an enumeration), and a predefined @ref SOElement. @tparam KeyType The type of key identifying the format. */ template class KnownFormats { public: /** A known format. */ class Item { public: Item( char const* name, KeyType type, std::initializer_list uniqueFields, std::initializer_list commonFields) : soTemplate_(uniqueFields, commonFields), name_(name), type_(type) { // Verify that KeyType is appropriate. static_assert( std::is_enum::value || std::is_integral::value, "KnownFormats KeyType must be integral or enum."); } /** Retrieve the name of the format. */ std::string const& getName() const { return name_; } /** Retrieve the transaction type this format represents. */ KeyType getType() const { return type_; } SOTemplate const& getSOTemplate() const { return soTemplate_; } private: SOTemplate soTemplate_; std::string const name_; KeyType const type_; }; /** Create the known formats object. Derived classes will load the object with all the known formats. */ KnownFormats() : name_(beast::type_name()) { } /** Destroy the known formats object. The defined formats are deleted. */ virtual ~KnownFormats() = default; KnownFormats(KnownFormats const&) = delete; KnownFormats& operator=(KnownFormats const&) = delete; /** Retrieve the type for a format specified by name. If the format name is unknown, an exception is thrown. @param name The name of the type. @return The type. */ KeyType findTypeByName(std::string const& name) const { if (auto const result = findByName(name)) return result->getType(); Throw( name_ + ": Unknown format name '" + name.substr(0, std::min(name.size(), std::size_t(32))) + "'"); } /** Retrieve a format based on its type. */ Item const* findByType(KeyType type) const { auto const itr = types_.find(type); if (itr == types_.end()) return nullptr; return itr->second; } // begin() and end() are provided for testing purposes. typename std::forward_list::const_iterator begin() const { return formats_.begin(); } typename std::forward_list::const_iterator end() const { return formats_.end(); } protected: /** Retrieve a format based on its name. */ Item const* findByName(std::string const& name) const { auto const itr = names_.find(name); if (itr == names_.end()) return nullptr; return itr->second; } /** Add a new format. @param name The name of this format. @param type The type of this format. @param uniqueFields An std::initializer_list of unique fields @param commonFields An std::initializer_list of common fields @return The created format. */ Item const& add(char const* name, KeyType type, std::initializer_list uniqueFields, std::initializer_list commonFields = {}) { if (auto const item = findByType(type)) { LogicError( std::string("Duplicate key for item '") + name + "': already maps to " + item->getName()); } formats_.emplace_front(name, type, uniqueFields, commonFields); Item const& item{formats_.front()}; names_[name] = &item; types_[type] = &item; return item; } private: std::string name_; // One of the situations where a std::forward_list is useful. We want to // store each Item in a place where its address won't change. So a node- // based container is appropriate. But we don't need searchability. std::forward_list formats_; boost::container::flat_map names_; boost::container::flat_map types_; }; } // namespace ripple #endif