Skip to content

src/scale/detail/fixed_witdh_integer.hpp

Namespaces

Name
sgns
sgns::scale
sgns::scale::detail

Functions

Name
template <class T ,class S ,typename I =std::decay_t,typename =std::enable_if_t>>
void
encodeInteger(T value, S & out)
template <class T ,class S ,typename I =std::decay_t,typename =std::enable_if_t>>
I
decodeInteger(S & stream)
decodeInteger function decodes integer from stream

Functions Documentation

function encodeInteger

template <class T ,
class S ,
typename I  =std::decay_t<T>,
typename  =std::enable_if_t<std::is_integral_v<I>>>
void encodeInteger(
    T value,
    S & out
)

Parameters:

  • value integer value
  • out output stream

Template Parameters:

  • T integer type
  • S output stream type

encodeInteger encodes any integer type to little-endian representation

function decodeInteger

template <class T ,
class S ,
typename I  =std::decay_t<T>,
typename  =std::enable_if_t<std::is_integral_v<I>>>
I decodeInteger(
    S & stream
)

decodeInteger function decodes integer from stream

Parameters:

  • stream source stream

Template Parameters:

  • T integer type

Return: decoded value or error

Source code

#ifndef SUPERGENIUS_SCALE_UTIL_HPP
#define SUPERGENIUS_SCALE_UTIL_HPP

#include <array>
#include <cstdint>

#include <boost/endian/arithmetic.hpp>

#include "base/outcome_throw.hpp"
#include "macro/unreachable.hpp"
#include "scale/scale_error.hpp"

namespace sgns::scale::detail {
  template <class T, class S, typename I = std::decay_t<T>, typename = std::enable_if_t<std::is_integral_v<I>>>
  void encodeInteger( T value, S &out )
  { // no need to take integers by &&
      constexpr size_t size = sizeof( T );
      constexpr size_t bits = size * 8;
      boost::endian::endian_buffer<boost::endian::order::little, T, bits> buf{};
      buf = value;  // cannot initialize, only assign
      for (size_t i = 0; i < size; ++i) {
          // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
          out << buf.data()[i];
      }
  }

  template <class T,
            class S,
            typename I = std::decay_t<T>,
            typename = std::enable_if_t<std::is_integral_v<I>>>
  I decodeInteger(S &stream) {
    constexpr size_t size = sizeof(I);
    static_assert(size <= 8);

    // clang-format off
    // sign bit = 2^(num_bits - 1)
    static constexpr std::array<uint64_t, 8> sign_bit = {
            0x80,               // 1 byte
            0x8000,             // 2 bytes
            0x800000,           // 3 bytes
            0x80000000,         // 4 bytes
            0x8000000000,       // 5 bytes
            0x800000000000,     // 6 bytes
            0x80000000000000,   // 7 bytes
            0x8000000000000000  // 8 bytes
    };

    static constexpr std::array<uint64_t, 8> multiplier = {
            0x1,                // 2^0
            0x100,              // 2^8
            0x10000,            // 2^16
            0x1000000,          // 2^24
            0x100000000,        // 2^32
            0x10000000000,      // 2^40
            0x1000000000000,    // 2^48
            0x100000000000000   // 2^56
    };
    // clang-format on

    if (!stream.hasMore(size)) {
      base::raise(DecodeError::NOT_ENOUGH_DATA);
      UNREACHABLE
    }

    // get integer as 4 bytes from little-endian stream
    // and represent it as native-endian unsigned int eger
    uint64_t v = 0u;

    for (size_t i = 0; i < size; ++i) {
      // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-constant-array-index)
      v += multiplier[i] * static_cast<uint64_t>(stream.nextByte());
    }
    // now we have uint64 native-endian value
    // which can be signed or unsigned under the cover

    // if it is unsigned, we know that is not greater than max value for type T
    // so static_cast<T>(v) is safe

    // if it is signed, but positive it is also ok
    // we can be sure that it is less than max_value<T>/2
    // to check whether is is negative we check if the sign bit present
    // in unsigned form it means that value is more than
    // a value 2^(bits_number-1)
    bool is_positive_signed = v < sign_bit[size - 1];
    if (std::is_unsigned<T>() || is_positive_signed) {
      return static_cast<T>(v);
    }

    // T is signed integer type and the value v is negative
    // value is negative signed means ( - x )
    // where x is positive unsigned < sign_bits[size-1]
    // find this x, safely cast to signed and negate result
    // the bitwise negation operation affects higher bits as well
    // but it doesn't spoil the result
    // static_cast to smaller size cuts them off
    T sv = -static_cast<T>((~v) + 1);

    return sv;
  }
}  // namespace sgns::scale::detail

#endif  // SUPERGENIUS_SCALE_UTIL_HPP

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