Skip to content

src/scale/scale_decoder_stream.hpp

Namespaces

Name
sgns
sgns::scale

Classes

Name
class sgns::scale::ScaleDecoderStream

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