src/scale/scale_decoder_stream.hpp
Namespaces
Classes
Source code
#ifndef SUPERGENIUS_SRC_SCALE_SCALE_DECODER_STREAM_HPP
#define SUPERGENIUS_SRC_SCALE_SCALE_DECODER_STREAM_HPP
#include <array>
#include <list>
#include <boost/variant/variant.hpp>
#include <boost/multiprecision/cpp_int.hpp>
#include <boost/optional.hpp>
#include <gsl/span>
#include "base/outcome_throw.hpp"
#include "scale/detail/fixed_witdh_integer.hpp"
#include "scale/types.hpp"
namespace sgns::scale {
class ScaleDecoderStream {
public:
// special tag to differentiate decoding streams from others
static constexpr auto is_decoder_stream = true;
explicit ScaleDecoderStream(gsl::span<const uint8_t> span);
template <class F, class S>
ScaleDecoderStream &operator>>(std::pair<F, S> &p) {
static_assert(!std::is_reference_v<F> && !std::is_reference_v<S>);
return *this >> const_cast<std::remove_const_t<F> &>(p.first) // NOLINT
>> const_cast<std::remove_const_t<S> &>(p.second); // NOLINT
}
template <class... T>
ScaleDecoderStream &operator>>(std::tuple<T...> &v) {
if constexpr (sizeof...(T) > 0) {
decodeElementOfTuple<0>(v);
}
return *this;
}
template <class... Ts>
ScaleDecoderStream &operator>>(boost::variant<Ts...> &v) {
// first byte means type index
uint8_t type_index = 0u;
*this >> type_index; // decode type index
// ensure that index is in [0, types_count)
if (type_index >= sizeof...(Ts)) {
base::raise(DecodeError::WRONG_TYPE_INDEX);
}
tryDecodeAsOneOfVariant<0>(v, type_index);
return *this;
}
template <class T>
ScaleDecoderStream &operator>>(std::shared_ptr<T> &v) {
using mutableT = std::remove_const_t<T>;
static_assert(std::is_default_constructible_v<mutableT>);
v = std::make_shared<mutableT>();
return *this >> const_cast<mutableT &>(*v); // NOLINT
}
template <class T>
ScaleDecoderStream &operator>>(std::unique_ptr<T> &v) {
using mutableT = std::remove_const_t<T>;
static_assert(std::is_default_constructible_v<mutableT>);
v = std::make_unique<mutableT>();
return *this >> const_cast<mutableT &>(*v); // NOLINT
}
template <typename T, typename I = std::decay_t<T>, typename = std::enable_if_t<std::is_integral_v<I>>>
ScaleDecoderStream &operator>>( T &v )
{
// check bool
if constexpr ( std::is_same<I, bool>::value )
{
v = decodeBool();
return *this;
}
// check byte
if constexpr ( sizeof( T ) == 1u )
{
v = nextByte();
return *this;
}
// decode any other integer
v = detail::decodeInteger<I>( *this );
return *this;
}
template <class T>
ScaleDecoderStream &operator>>(boost::optional<T> &v) {
using mutableT = std::remove_const_t<T>;
static_assert(std::is_default_constructible_v<mutableT>);
// optional bool is special case of optional values
// it is encoded as one byte instead of two
// as described in specification
if constexpr (std::is_same<mutableT, bool>::value) {
v = decodeOptionalBool();
return *this;
}
// detect if optional has value
bool has_value = false;
*this >> has_value;
if (!has_value) {
v.reset();
return *this;
}
// decode value
v.emplace();
return *this >> const_cast<mutableT &>(*v); // NOLINT
}
ScaleDecoderStream &operator>>(CompactInteger &v);
template <class T>
ScaleDecoderStream &operator>>(std::vector<T> &v) {
using mutableT = std::remove_const_t<T>;
using size_type = typename std::list<T>::size_type;
static_assert(std::is_default_constructible_v<mutableT>);
CompactInteger size{0u};
*this >> size;
if (size > std::numeric_limits<size_type>::max()) {
base::raise(DecodeError::TOO_MANY_ITEMS);
}
auto item_count = size.convert_to<size_type>();
std::vector<mutableT> vec;
vec.resize(item_count);
for (size_type i = 0u; i < item_count; ++i) {
*this >> vec[i];
}
v = std::move(vec);
return *this;
}
template <class T>
ScaleDecoderStream &operator>>(std::list<T> &v) {
using mutableT = std::remove_const_t<T>;
using size_type = typename std::list<T>::size_type;
static_assert(std::is_default_constructible_v<mutableT>);
CompactInteger size{0u};
*this >> size;
if (size > std::numeric_limits<size_type>::max()) {
base::raise(DecodeError::TOO_MANY_ITEMS);
}
auto item_count = size.convert_to<size_type>();
std::list<T> lst;
lst.reserve(item_count);
for (size_type i = 0u; i < item_count; ++i) {
lst.emplace_back();
*this >> lst.back();
}
v = std::move(lst);
return *this;
}
template <class T, size_t size>
ScaleDecoderStream &operator>>(std::array<T, size> &a) {
using mutableT = std::remove_const_t<T>;
for (size_t i = 0u; i < size; ++i) {
*this >> const_cast<mutableT &>(a[i]); // NOLINT
}
return *this;
}
ScaleDecoderStream &operator>>(boost::multiprecision::uint256_t &i) {
// TODO(akvinikym) PRE-285: maybe move to another file and implement it
return *this;
}
ScaleDecoderStream &operator>>(std::string &v);
bool hasMore(uint64_t n) const;
uint8_t nextByte();
private:
bool decodeBool();
boost::optional<bool> decodeOptionalBool();
template <size_t I, class... Ts>
void decodeElementOfTuple(std::tuple<Ts...> &v) {
using T = std::remove_const_t<std::tuple_element_t<I, std::tuple<Ts...>>>;
*this >> const_cast<T &>(std::get<I>(v)); // NOLINT
if constexpr (sizeof...(Ts) > I + 1) {
decodeElementOfTuple<I + 1>(v);
}
}
template <size_t I, class... Ts>
void tryDecodeAsOneOfVariant(boost::variant<Ts...> &v, size_t i) {
using T = std::remove_const_t<std::tuple_element_t<I, std::tuple<Ts...>>>;
static_assert(std::is_default_constructible_v<T>);
if (I == i) {
T val;
*this >> val;
v = std::forward<T>(val);
return;
}
if constexpr (sizeof...(Ts) > I + 1) {
tryDecodeAsOneOfVariant<I + 1>(v, i);
}
}
using ByteSpan = gsl::span<const uint8_t>;
using SpanIterator = ByteSpan::const_iterator;
using SizeType = ByteSpan::size_type;
ByteSpan span_;
SpanIterator current_iterator_;
SizeType current_index_;
};
} // namespace sgns::scale
#endif // SUPERGENIUS_SRC_SCALE_SCALE_DECODER_STREAM_HPP
Updated on 2026-03-04 at 13:10:45 -0800