Skip to content

src/scale/scale_encoder_stream.hpp

Namespaces

Name
sgns
sgns::scale

Classes

Name
class sgns::scale::ScaleEncoderStream
Scale-encodes data into a byte stream.

Source code

#ifndef SUPERGENIUS_SRC_SCALE_SCALE_ENCODER_STREAM_HPP
#define SUPERGENIUS_SRC_SCALE_SCALE_ENCODER_STREAM_HPP

#include <deque>
#include <list>

#include <boost/variant/get.hpp>
#include <boost/variant/variant.hpp>
#include <boost/optional.hpp>
#include <gsl/span>

#include "scale/detail/fixed_witdh_integer.hpp"
#include "scale/types.hpp"

namespace sgns::scale {
  class ScaleEncoderStream {
   public:
    // special tag to differentiate encoding streams from others
    static constexpr auto is_encoder_stream = true;


    std::vector<uint8_t> data() const;

    template <class F, class S>
    ScaleEncoderStream &operator<<(const std::pair<F, S> &p) {
      return *this << p.first << p.second;
    }

    template <class... Ts>
    ScaleEncoderStream &operator<<(const std::tuple<Ts...> &v) {
      if constexpr (sizeof...(Ts) > 0) {
        encodeElementOfTuple<0>(v);
      }
      return *this;
    }

    template <class... T>
    ScaleEncoderStream &operator<<(const boost::variant<T...> &v) {
      tryEncodeAsOneOfVariant<0>(v);
      return *this;
    }

    template <class T>
    ScaleEncoderStream &operator<<(const std::shared_ptr<T> &v) {
      if (v == nullptr) {
        base::raise(EncodeError::DEREF_NULLPOINTER);
      }
      return *this << *v;
    }

    template <class T>
    ScaleEncoderStream &operator<<(const std::unique_ptr<T> &v) {
      if (v == nullptr) {
        base::raise(EncodeError::DEREF_NULLPOINTER);
      }
      return *this << *v;
    }

    template <class T>
    ScaleEncoderStream &operator<<(const std::vector<T> &c) {
      return encodeCollection(c.size(), c.begin(), c.end());
    }

    template <class T>
    ScaleEncoderStream &operator<<(const std::list<T> &c) {
      return encodeCollection(c.size(), c.begin(), c.end());
    }

    template <class T>
    ScaleEncoderStream &operator<<(const boost::optional<T> &v) {
      // optional bool is a special case of optional values
      // it should be encoded using one byte instead of two
      // as described in specification
      if constexpr (std::is_same<T, bool>::value) {
        return encodeOptionalBool(v);
      }
      if (!v.has_value()) {
        return putByte(0u);
      }
      return putByte(1u) << *v;
    }

    template <class T>
    ScaleEncoderStream &operator<<(const gsl::span<T> &v) {
      return encodeCollection(v.size(), v.begin(), v.end());
    }

    template <typename T, size_t size>
    ScaleEncoderStream &operator<<(const std::array<T, size> &a) {
      // TODO(akvinikym) PRE-285: bad implementation: maybe move to another file
      // and implement it
      return encodeCollection(size, a.begin(), a.end());
    }

    ScaleEncoderStream &operator<<(const boost::multiprecision::uint256_t &i) {
      // TODO(akvinikym) PRE-285: maybe move to another file and implement it
      return *this;
    }

    template <class T>
    ScaleEncoderStream &operator<<(const std::reference_wrapper<T> &v) {
      return *this << static_cast<const T &>(v);
    }

    ScaleEncoderStream &operator<<(std::string_view sv) {
      return encodeCollection(sv.size(), sv.begin(), sv.end());
    }

    template <typename T, typename I = std::decay_t<T>, typename = std::enable_if_t<std::is_integral_v<I>>>
    ScaleEncoderStream &operator<<( T &&v )
    {
        // encode bool
        if constexpr ( std::is_same<I, bool>::value )
        {
            uint8_t byte = ( v ? 1u : 0u );
            return putByte( byte );
        }
        // put byte
        if constexpr ( sizeof( T ) == 1u )
        {
            // to avoid infinite recursion
            return putByte( static_cast<uint8_t>( v ) );
        }
        // encode any other integer
        detail::encodeInteger<I>( v, *this );
        return *this;
    }

    ScaleEncoderStream &operator<<(const CompactInteger &v);

   protected:
    template <size_t I, class... Ts>
    void encodeElementOfTuple(const std::tuple<Ts...> &v) {
      *this << std::get<I>(v);
      if constexpr (sizeof...(Ts) > I + 1) {
        encodeElementOfTuple<I + 1>(v);
      }
    }

    template <uint8_t I, class... Ts>
    void tryEncodeAsOneOfVariant(const boost::variant<Ts...> &v) {
      using T = std::tuple_element_t<I, std::tuple<Ts...>>;
      if (v.type() == typeid(T)) {
        *this << I << boost::get<T>(v);
        return;
      }
      if constexpr (sizeof...(Ts) > I + 1) {
        tryEncodeAsOneOfVariant<I + 1>(v);
      }
    }

    template <class It>
    ScaleEncoderStream &encodeCollection(const CompactInteger &size,
                                         It &&begin,
                                         It &&end) {
      *this << size;
      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
      for (auto &&it = begin; it != end; ++it) {
        *this << *it;
      }
      return *this;
    }


    ScaleEncoderStream &putByte(uint8_t v);

   private:
    ScaleEncoderStream &encodeOptionalBool(const boost::optional<bool> &v);
    std::deque<uint8_t> stream_;
  };

}  // namespace sgns::scale

#endif  // SUPERGENIUS_SRC_SCALE_SCALE_ENCODER_STREAM_HPP

Updated on 2026-03-04 at 13:10:45 -0800