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 void |
encodeInteger(T value, S & out) |
| template <class T ,class S ,typename I =std::decay_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