233 lines
6.6 KiB
C++
233 lines
6.6 KiB
C++
#pragma once
|
|
#include "../utility/assert.h"
|
|
#include "../utility/bits.h"
|
|
#include "../utility/meta.h"
|
|
#include "../utility/parameter.h"
|
|
|
|
#include "../stream/serialize_traits.h"
|
|
|
|
#include <cstdint>
|
|
#include <limits>
|
|
#include <type_traits>
|
|
|
|
namespace bitstream
|
|
{
|
|
/**
|
|
* @brief Wrapper type for compiletime known integer bounds
|
|
* @tparam T
|
|
*/
|
|
template<typename T, T = (std::numeric_limits<T>::min)(), T = (std::numeric_limits<T>::max)()>
|
|
struct bounded_int;
|
|
|
|
#pragma region const integral types
|
|
/**
|
|
* @brief A trait used to serialize integer values with compiletime bounds
|
|
* @tparam T A type matching an integer value
|
|
* @tparam Min The lower bound. Inclusive
|
|
* @tparam Max The upper bound. Inclusive
|
|
*/
|
|
template<typename T, T Min, T Max>
|
|
struct serialize_traits<bounded_int<T, Min, Max>, typename std::enable_if_t<std::is_integral_v<T> && !std::is_const_v<T>>>
|
|
{
|
|
static_assert(sizeof(T) <= 8, "Integers larger than 8 bytes are currently not supported. You will have to write this functionality yourself");
|
|
|
|
/**
|
|
* @brief Writes an integer into the @p writer
|
|
* @param writer The stream to write to
|
|
* @param value The value to serialize
|
|
* @return Success
|
|
*/
|
|
template<typename Stream>
|
|
typename utility::is_writing_t<Stream>
|
|
static serialize(Stream& writer, in<T> value) noexcept
|
|
{
|
|
static_assert(Min < Max);
|
|
|
|
BS_ASSERT(value >= Min && value <= Max);
|
|
|
|
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);
|
|
|
|
static_assert(num_bits <= sizeof(T) * 8);
|
|
|
|
if constexpr (sizeof(T) > 4 && num_bits > 32)
|
|
{
|
|
// If the given range is bigger than a word (32 bits)
|
|
uint32_t unsigned_value = static_cast<uint32_t>(value - Min);
|
|
BS_ASSERT(writer.serialize_bits(unsigned_value, 32));
|
|
|
|
unsigned_value = static_cast<uint32_t>((value - Min) >> 32);
|
|
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits - 32));
|
|
}
|
|
else
|
|
{
|
|
// If the given range is smaller than or equal to a word (32 bits)
|
|
uint32_t unsigned_value = static_cast<uint32_t>(value - Min);
|
|
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits));
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Reads an integer from the @p writer into @p value
|
|
* @param reader The stream to read from
|
|
* @param value The value to serialize
|
|
* @return Success
|
|
*/
|
|
template<typename Stream>
|
|
typename utility::is_reading_t<Stream>
|
|
static serialize(Stream& reader, T& value) noexcept
|
|
{
|
|
static_assert(Min < Max);
|
|
|
|
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);
|
|
|
|
static_assert(num_bits <= sizeof(T) * 8);
|
|
|
|
if constexpr (sizeof(T) > 4 && num_bits > 32)
|
|
{
|
|
// If the given range is bigger than a word (32 bits)
|
|
value = 0;
|
|
uint32_t unsigned_value;
|
|
|
|
BS_ASSERT(reader.serialize_bits(unsigned_value, 32));
|
|
value |= static_cast<T>(unsigned_value);
|
|
|
|
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits - 32));
|
|
value |= static_cast<T>(unsigned_value) << 32;
|
|
|
|
value += Min;
|
|
}
|
|
else
|
|
{
|
|
// If the given range is smaller than or equal to a word (32 bits)
|
|
uint32_t unsigned_value;
|
|
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits));
|
|
|
|
value = static_cast<T>(unsigned_value) + Min;
|
|
}
|
|
|
|
BS_ASSERT(value >= Min && value <= Max);
|
|
|
|
return true;
|
|
}
|
|
};
|
|
#pragma endregion
|
|
|
|
#pragma region integral types
|
|
/**
|
|
* @brief A trait used to serialize integer values with runtime bounds
|
|
* @tparam T A type matching an integer value
|
|
*/
|
|
template<typename T>
|
|
struct serialize_traits<T, typename std::enable_if_t<std::is_integral_v<T> && !std::is_const_v<T>>>
|
|
{
|
|
static_assert(sizeof(T) <= 8, "Integers larger than 8 bytes are currently not supported. You will have to write this functionality yourself");
|
|
|
|
/**
|
|
* @brief Writes an integer into the @p writer
|
|
* @param writer The stream to write to
|
|
* @param value The value to serialize
|
|
* @param min The minimum bound that @p value can be. Inclusive
|
|
* @param max The maximum bound that @p value can be. Inclusive
|
|
* @return Success
|
|
*/
|
|
template<typename Stream>
|
|
typename utility::is_writing_t<Stream>
|
|
static serialize(Stream& writer, in<T> value, T min, T max) noexcept
|
|
{
|
|
BS_ASSERT(min < max);
|
|
|
|
BS_ASSERT(value >= min && value <= max);
|
|
|
|
uint32_t num_bits = utility::bits_in_range(min, max);
|
|
|
|
BS_ASSERT(num_bits <= sizeof(T) * 8);
|
|
|
|
if constexpr (sizeof(T) > 4)
|
|
{
|
|
if (num_bits > 32)
|
|
{
|
|
// If the given range is bigger than a word (32 bits)
|
|
uint32_t unsigned_value = static_cast<uint32_t>(value - min);
|
|
BS_ASSERT(writer.serialize_bits(unsigned_value, 32));
|
|
|
|
unsigned_value = static_cast<uint32_t>((value - min) >> 32);
|
|
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits - 32));
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If the given range is smaller than or equal to a word (32 bits)
|
|
uint32_t unsigned_value = static_cast<uint32_t>(value - min);
|
|
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits));
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Reads an integer from the @p reader into @p value
|
|
* @param reader The stream to read from
|
|
* @param value The value to read into
|
|
* @param min The minimum bound that @p value can be. Inclusive
|
|
* @param max The maximum bound that @p value can be. Inclusive
|
|
* @return Success
|
|
*/
|
|
template<typename Stream>
|
|
typename utility::is_reading_t<Stream>
|
|
static serialize(Stream& reader, T& value, T min, T max) noexcept
|
|
{
|
|
BS_ASSERT(min < max);
|
|
|
|
uint32_t num_bits = utility::bits_in_range(min, max);
|
|
|
|
BS_ASSERT(num_bits <= sizeof(T) * 8);
|
|
|
|
if constexpr (sizeof(T) > 4)
|
|
{
|
|
if (num_bits > 32)
|
|
{
|
|
// If the given range is bigger than a word (32 bits)
|
|
value = 0;
|
|
uint32_t unsigned_value;
|
|
|
|
BS_ASSERT(reader.serialize_bits(unsigned_value, 32));
|
|
value |= static_cast<T>(unsigned_value);
|
|
|
|
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits - 32));
|
|
value |= static_cast<T>(unsigned_value) << 32;
|
|
|
|
value += min;
|
|
|
|
BS_ASSERT(value >= min && value <= max);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
// If the given range is smaller than or equal to a word (32 bits)
|
|
uint32_t unsigned_value;
|
|
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits));
|
|
|
|
value = static_cast<T>(unsigned_value) + min;
|
|
|
|
BS_ASSERT(value >= min && value <= max);
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @brief Writes or reads an integer into the @p stream
|
|
* @param stream The stream to serialize to/from
|
|
* @param value The value to serialize
|
|
* @return Success
|
|
*/
|
|
template<typename Stream, typename U>
|
|
static bool serialize(Stream& stream, U&& value) noexcept
|
|
{
|
|
return serialize_traits<bounded_int<T>>::serialize(stream, std::forward<U>(value));
|
|
}
|
|
};
|
|
#pragma endregion
|
|
} |