Files
rippled/include/xrpl/protocol/KnownFormats.h
2025-10-23 11:04:30 -04:00

189 lines
4.8 KiB
C++

#ifndef XRPL_PROTOCOL_KNOWNFORMATS_H_INCLUDED
#define XRPL_PROTOCOL_KNOWNFORMATS_H_INCLUDED
#include <xrpl/basics/contract.h>
#include <xrpl/beast/type_name.h>
#include <xrpl/protocol/SOTemplate.h>
#include <boost/container/flat_map.hpp>
#include <algorithm>
#include <forward_list>
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 KeyType, class Derived>
class KnownFormats
{
public:
/** A known format.
*/
class Item
{
public:
Item(
char const* name,
KeyType type,
std::initializer_list<SOElement> uniqueFields,
std::initializer_list<SOElement> commonFields)
: soTemplate_(uniqueFields, commonFields), name_(name), type_(type)
{
// Verify that KeyType is appropriate.
static_assert(
std::is_enum<KeyType>::value ||
std::is_integral<KeyType>::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<Derived>())
{
}
/** 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<std::runtime_error>(
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<Item>::const_iterator
begin() const
{
return formats_.begin();
}
typename std::forward_list<Item>::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<SOElement> uniqueFields,
std::initializer_list<SOElement> 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<Item> formats_;
boost::container::flat_map<std::string, Item const*> names_;
boost::container::flat_map<KeyType, Item const*> types_;
};
} // namespace ripple
#endif