Attempting to use an external library to handle bitstreams. Isn't going the grreatest.
This commit is contained in:
@@ -0,0 +1,165 @@
|
||||
#pragma once
|
||||
#include "../utility/assert.h"
|
||||
#include "../utility/meta.h"
|
||||
#include "../utility/parameter.h"
|
||||
|
||||
#include "../stream/serialize_traits.h"
|
||||
|
||||
#include "../traits/bool_trait.h"
|
||||
#include "../traits/integral_traits.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace bitstream
|
||||
{
|
||||
/**
|
||||
* @brief Wrapper type for subsets of arrays
|
||||
* @tparam T The type of the array
|
||||
*/
|
||||
template<typename T, typename = T>
|
||||
struct array_subset;
|
||||
|
||||
/**
|
||||
* @brief A trait used for serializing a subset of an array of objects
|
||||
* @tparam T The type of the object in the array
|
||||
* @tparam Trait
|
||||
*/
|
||||
template<typename T, typename Trait>
|
||||
struct serialize_traits<array_subset<T, Trait>>
|
||||
{
|
||||
private:
|
||||
template<uint32_t Min, uint32_t Max, typename Stream>
|
||||
static bool serialize_difference(Stream& stream, int& previous, int& current, uint32_t& difference)
|
||||
{
|
||||
bool use_bits;
|
||||
if constexpr (Stream::writing)
|
||||
use_bits = difference <= Max;
|
||||
BS_ASSERT(stream.template serialize<bool>(use_bits));
|
||||
if (use_bits)
|
||||
{
|
||||
using bounded_trait = bounded_int<uint32_t, Min, Max>;
|
||||
|
||||
BS_ASSERT(stream.template serialize<bounded_trait>(difference));
|
||||
if constexpr (Stream::reading)
|
||||
current = previous + difference;
|
||||
previous = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
static bool serialize_index(Stream& stream, int& previous, int& current, int max_size)
|
||||
{
|
||||
uint32_t difference;
|
||||
if constexpr (Stream::writing)
|
||||
{
|
||||
BS_ASSERT(previous < current);
|
||||
difference = current - previous;
|
||||
BS_ASSERT(difference > 0);
|
||||
}
|
||||
|
||||
// +1 (1 bit)
|
||||
bool plus_one;
|
||||
if constexpr (Stream::writing)
|
||||
plus_one = difference == 1;
|
||||
BS_ASSERT(stream.template serialize<bool>(plus_one));
|
||||
if (plus_one)
|
||||
{
|
||||
if constexpr (Stream::reading)
|
||||
current = previous + 1;
|
||||
previous = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
// [+2,5] -> [0,3] (2 bits)
|
||||
if (serialize_difference<2, 5>(stream, previous, current, difference))
|
||||
return true;
|
||||
|
||||
// [6,13] -> [0,7] (3 bits)
|
||||
if (serialize_difference<6, 13>(stream, previous, current, difference))
|
||||
return true;
|
||||
|
||||
// [14,29] -> [0,15] (4 bits)
|
||||
if (serialize_difference<14, 29>(stream, previous, current, difference))
|
||||
return true;
|
||||
|
||||
// [30,61] -> [0,31] (5 bits)
|
||||
if (serialize_difference<30, 61>(stream, previous, current, difference))
|
||||
return true;
|
||||
|
||||
// [62,125] -> [0,63] (6 bits)
|
||||
if (serialize_difference<62, 125>(stream, previous, current, difference))
|
||||
return true;
|
||||
|
||||
// [126,MaxObjects+1]
|
||||
BS_ASSERT(stream.template serialize<uint32_t>(difference, 126, max_size));
|
||||
if constexpr (Stream::reading)
|
||||
current = previous + difference;
|
||||
previous = current;
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Writes a subset of the array @p values into the writer
|
||||
* @tparam Compare A function type which returns a bool
|
||||
* @tparam ...Args The types of any additional arguments
|
||||
* @param writer The stream to write to
|
||||
* @param values The array of objects to serialize
|
||||
* @param max_size The size of the array
|
||||
* @param compare A function which returns true if the object should be written, false otherwise
|
||||
* @param ...args Any additional arguments to use when serializing each individual object
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream, typename Compare, typename... Args>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, T* values, int max_size, Compare compare, Args&&... args) noexcept
|
||||
{
|
||||
int prev_index = -1;
|
||||
for (int index = 0; index < max_size; index++)
|
||||
{
|
||||
if (!compare(values[index]))
|
||||
continue;
|
||||
|
||||
BS_ASSERT(serialize_index(writer, prev_index, index, max_size));
|
||||
|
||||
BS_ASSERT(writer.template serialize<Trait>(values[index], std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
BS_ASSERT(serialize_index(writer, prev_index, max_size, max_size));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Writes a subset of a serialized array into @p values
|
||||
* @tparam ...Args The types of any additional arguments
|
||||
* @param reader The stream to read from
|
||||
* @param values The array of objects to read into
|
||||
* @param max_size The size of the array
|
||||
* @param compare Not used, but kept for compatibility with the serialize write function
|
||||
* @param ...args Any additional arguments to use when serializing each individual object
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream, typename... Args>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, T* values, int max_size, Args&&... args) noexcept
|
||||
{
|
||||
int prev_index = -1;
|
||||
int index = 0;
|
||||
while (true)
|
||||
{
|
||||
BS_ASSERT(serialize_index(reader, prev_index, index, max_size));
|
||||
|
||||
if (index == max_size)
|
||||
break;
|
||||
|
||||
BS_ASSERT(reader.template serialize<Trait>(values[index], std::forward<Args>(args)...));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include "../utility/assert.h"
|
||||
#include "../utility/meta.h"
|
||||
#include "../utility/parameter.h"
|
||||
|
||||
#include "../stream/serialize_traits.h"
|
||||
|
||||
namespace bitstream
|
||||
{
|
||||
/**
|
||||
* @brief A trait used to serialize a boolean as a single bit
|
||||
*/
|
||||
template<>
|
||||
struct serialize_traits<bool>
|
||||
{
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, in<bool> value) noexcept
|
||||
{
|
||||
uint32_t unsigned_value = value;
|
||||
|
||||
return writer.serialize_bits(unsigned_value, 1U);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, out<bool> value) noexcept
|
||||
{
|
||||
uint32_t unsigned_value;
|
||||
|
||||
BS_ASSERT(reader.serialize_bits(unsigned_value, 1U));
|
||||
|
||||
value = unsigned_value;
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A trait used to serialize multiple boolean values
|
||||
*/
|
||||
template<size_t Size>
|
||||
struct serialize_traits<bool[Size]>
|
||||
{
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, const bool* values) noexcept
|
||||
{
|
||||
uint32_t unsigned_value;
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
{
|
||||
unsigned_value = values[i];
|
||||
BS_ASSERT(writer.serialize_bits(unsigned_value, 1U));
|
||||
}
|
||||
|
||||
return writer.serialize_bits(unsigned_value, 1U);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, bool* values) noexcept
|
||||
{
|
||||
uint32_t unsigned_value;
|
||||
for (size_t i = 0; i < Size; i++)
|
||||
{
|
||||
BS_ASSERT(reader.serialize_bits(unsigned_value, 1U));
|
||||
|
||||
values[i] = unsigned_value;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
#include "../utility/assert.h"
|
||||
#include "../utility/meta.h"
|
||||
#include "../utility/parameter.h"
|
||||
|
||||
#include "../stream/serialize_traits.h"
|
||||
|
||||
#include "../traits/integral_traits.h"
|
||||
|
||||
namespace bitstream
|
||||
{
|
||||
/**
|
||||
* @brief Wrapper type for compiletime known integer bounds
|
||||
* @tparam T
|
||||
*/
|
||||
template<typename T, std::underlying_type_t<T> = (std::numeric_limits<T>::min)(), std::underlying_type_t<T> = (std::numeric_limits<T>::max)()>
|
||||
struct bounded_enum;
|
||||
|
||||
/**
|
||||
* @brief A trait used to serialize an enum type with runtime bounds
|
||||
*/
|
||||
template<typename T>
|
||||
struct serialize_traits<T, typename std::enable_if_t<std::is_enum_v<T>>>
|
||||
{
|
||||
using value_type = std::underlying_type_t<T>;
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, T value, value_type min = 0, value_type max = (std::numeric_limits<value_type>::max)()) noexcept
|
||||
{
|
||||
value_type unsigned_value = static_cast<value_type>(value);
|
||||
|
||||
return writer.template serialize<value_type>(unsigned_value, min, max);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, T& value, value_type min = 0, value_type max = (std::numeric_limits<value_type>::max)()) noexcept
|
||||
{
|
||||
value_type unsigned_value;
|
||||
|
||||
BS_ASSERT(reader.template serialize<value_type>(unsigned_value, min, max));
|
||||
|
||||
value = static_cast<T>(unsigned_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A trait used to serialize an enum type with compiletime bounds
|
||||
*/
|
||||
template<typename T, std::underlying_type_t<T> Min, std::underlying_type_t<T> Max>
|
||||
struct serialize_traits<bounded_enum<T, Min, Max>, typename std::enable_if_t<std::is_enum_v<T>>>
|
||||
{
|
||||
using value_type = std::underlying_type_t<T>;
|
||||
using bound_type = bounded_int<value_type, Min, Max>;
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, T value) noexcept
|
||||
{
|
||||
value_type unsigned_value = static_cast<value_type>(value);
|
||||
|
||||
return writer.template serialize<bound_type>(unsigned_value);
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, T& value) noexcept
|
||||
{
|
||||
value_type unsigned_value;
|
||||
|
||||
BS_ASSERT(reader.template serialize<bound_type>(unsigned_value));
|
||||
|
||||
value = static_cast<T>(unsigned_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
#pragma once
|
||||
#include "../utility/assert.h"
|
||||
#include "../utility/meta.h"
|
||||
#include "../utility/parameter.h"
|
||||
|
||||
#include "../stream/serialize_traits.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
|
||||
namespace bitstream
|
||||
{
|
||||
/**
|
||||
* @brief A trait used to serialize a float as-is, without any bound checking or quantization
|
||||
*/
|
||||
template<>
|
||||
struct serialize_traits<float>
|
||||
{
|
||||
/**
|
||||
* @brief Serializes a whole float into the writer
|
||||
* @param writer The stream to write to
|
||||
* @param value The float to serialize
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, in<float> value) noexcept
|
||||
{
|
||||
uint32_t tmp;
|
||||
std::memcpy(&tmp, &value, sizeof(float));
|
||||
|
||||
BS_ASSERT(writer.serialize_bits(tmp, 32));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serializes a whole float from the reader
|
||||
* @param reader The stream to read from
|
||||
* @param value The float to serialize to
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, float& value) noexcept
|
||||
{
|
||||
uint32_t tmp;
|
||||
|
||||
BS_ASSERT(reader.serialize_bits(tmp, 32));
|
||||
|
||||
std::memcpy(&value, &tmp, sizeof(float));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A trait used to serialize a double as-is, without any bound checking or quantization
|
||||
*/
|
||||
template<>
|
||||
struct serialize_traits<double>
|
||||
{
|
||||
/**
|
||||
* @brief Serializes a whole double into the writer
|
||||
* @param writer The stream to write to
|
||||
* @param value The double to serialize
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, in<double> value) noexcept
|
||||
{
|
||||
uint32_t tmp[2];
|
||||
std::memcpy(tmp, &value, sizeof(double));
|
||||
|
||||
BS_ASSERT(writer.serialize_bits(tmp[0], 32));
|
||||
BS_ASSERT(writer.serialize_bits(tmp[1], 32));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Serializes a whole double from the reader
|
||||
* @param reader The stream to read from
|
||||
* @param value The double to serialize to
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, double& value) noexcept
|
||||
{
|
||||
uint32_t tmp[2];
|
||||
|
||||
BS_ASSERT(reader.serialize_bits(tmp[0], 32));
|
||||
BS_ASSERT(reader.serialize_bits(tmp[1], 32));
|
||||
|
||||
std::memcpy(&value, tmp, sizeof(double));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
#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
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
#include "../quantization/bounded_range.h"
|
||||
#include "../quantization/half_precision.h"
|
||||
#include "../quantization/smallest_three.h"
|
||||
#include "../utility/assert.h"
|
||||
#include "../utility/meta.h"
|
||||
#include "../utility/parameter.h"
|
||||
|
||||
#include "../stream/serialize_traits.h"
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace bitstream
|
||||
{
|
||||
/**
|
||||
* @brief A trait used to serialize a single-precision float as half-precision
|
||||
*/
|
||||
template<>
|
||||
struct serialize_traits<half_precision>
|
||||
{
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& stream, in<float> value) noexcept
|
||||
{
|
||||
uint32_t int_value = half_precision::quantize(value);
|
||||
|
||||
BS_ASSERT(stream.serialize_bits(int_value, 16));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& stream, out<float> value) noexcept
|
||||
{
|
||||
uint32_t int_value;
|
||||
|
||||
BS_ASSERT(stream.serialize_bits(int_value, 16));
|
||||
|
||||
value = half_precision::dequantize(int_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A trait used to quantize and serialize a float to be within a given range and precision
|
||||
*/
|
||||
template<>
|
||||
struct serialize_traits<bounded_range>
|
||||
{
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& stream, in<bounded_range> range, in<float> value) noexcept
|
||||
{
|
||||
uint32_t int_value = range.quantize(value);
|
||||
|
||||
BS_ASSERT(stream.serialize_bits(int_value, range.get_bits_required()));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& stream, in<bounded_range> range, out<float> value) noexcept
|
||||
{
|
||||
uint32_t int_value;
|
||||
|
||||
BS_ASSERT(stream.serialize_bits(int_value, range.get_bits_required()));
|
||||
|
||||
value = range.dequantize(int_value);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A trait used to quantize and serialize quaternions using the smallest-three algorithm
|
||||
*/
|
||||
template<typename Q, size_t BitsPerElement>
|
||||
struct serialize_traits<smallest_three<Q, BitsPerElement>>
|
||||
{
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& stream, in<Q> value) noexcept
|
||||
{
|
||||
quantized_quaternion quantized_quat = smallest_three<Q, BitsPerElement>::quantize(value);
|
||||
|
||||
BS_ASSERT(stream.serialize_bits(quantized_quat.m, 2));
|
||||
BS_ASSERT(stream.serialize_bits(quantized_quat.a, BitsPerElement));
|
||||
BS_ASSERT(stream.serialize_bits(quantized_quat.b, BitsPerElement));
|
||||
BS_ASSERT(stream.serialize_bits(quantized_quat.c, BitsPerElement));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& stream, out<Q> value) noexcept
|
||||
{
|
||||
quantized_quaternion quantized_quat;
|
||||
|
||||
BS_ASSERT(stream.serialize_bits(quantized_quat.m, 2));
|
||||
BS_ASSERT(stream.serialize_bits(quantized_quat.a, BitsPerElement));
|
||||
BS_ASSERT(stream.serialize_bits(quantized_quat.b, BitsPerElement));
|
||||
BS_ASSERT(stream.serialize_bits(quantized_quat.c, BitsPerElement));
|
||||
|
||||
value = smallest_three<Q, BitsPerElement>::dequantize(quantized_quat);
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,344 @@
|
||||
#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 <string>
|
||||
|
||||
namespace bitstream
|
||||
{
|
||||
/**
|
||||
* @brief Wrapper type for compiletime known string max_size
|
||||
*/
|
||||
template<typename T, size_t I>
|
||||
struct bounded_string;
|
||||
|
||||
#pragma region const char*
|
||||
/**
|
||||
* @brief A trait used to serialize bounded c-style strings
|
||||
*/
|
||||
template<>
|
||||
struct serialize_traits<const char*>
|
||||
{
|
||||
/**
|
||||
* @brief Writes a c-style string into the @p writer
|
||||
* @param writer The stream to write to
|
||||
* @param value The string to serialize
|
||||
* @param max_size The maximum expected length of the string, including the null terminator
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, const char* value, uint32_t max_size) noexcept
|
||||
{
|
||||
uint32_t length = static_cast<uint32_t>(std::char_traits<char>::length(value));
|
||||
|
||||
BS_ASSERT(length < max_size);
|
||||
|
||||
if (length == 0)
|
||||
return true;
|
||||
|
||||
uint32_t num_bits = utility::bits_to_represent(max_size);
|
||||
|
||||
BS_ASSERT(writer.serialize_bits(length, num_bits));
|
||||
|
||||
return writer.serialize_bytes(reinterpret_cast<const uint8_t*>(value), length * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a c-style string from the @p reader into @p value
|
||||
* @param reader The stream to read from
|
||||
* @param value A pointer to the buffer that should be read into. The size of this buffer should be at least @p max_size
|
||||
* @param max_size The maximum expected length of the string, including the null terminator
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, char* value, uint32_t max_size) noexcept
|
||||
{
|
||||
uint32_t num_bits = utility::bits_to_represent(max_size);
|
||||
|
||||
uint32_t length;
|
||||
BS_ASSERT(reader.serialize_bits(length, num_bits));
|
||||
|
||||
BS_ASSERT(length < max_size);
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
value[0] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
BS_ASSERT(reader.serialize_bytes(reinterpret_cast<uint8_t*>(value), length * 8));
|
||||
|
||||
value[length] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A trait used to serialize bounded c-style strings with compiletime bounds
|
||||
* @tparam MaxSize The maximum expected length of the string, including the null terminator
|
||||
*/
|
||||
template<size_t MaxSize>
|
||||
struct serialize_traits<bounded_string<const char*, MaxSize>>
|
||||
{
|
||||
/**
|
||||
* @brief Writes a c-style string into the @p writer
|
||||
* @param writer The stream to write to
|
||||
* @param value The string to serialize
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, const char* value) noexcept
|
||||
{
|
||||
uint32_t length = static_cast<uint32_t>(std::char_traits<char>::length(value));
|
||||
|
||||
BS_ASSERT(length < MaxSize);
|
||||
|
||||
if (length == 0)
|
||||
return true;
|
||||
|
||||
constexpr uint32_t num_bits = utility::bits_to_represent(MaxSize);
|
||||
|
||||
BS_ASSERT(writer.serialize_bits(length, num_bits));
|
||||
|
||||
return writer.serialize_bytes(reinterpret_cast<const uint8_t*>(value), length * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a c-style string from the @p reader into @p value
|
||||
* @param reader The stream to read from
|
||||
* @param value A pointer to the buffer that should be read into. The size of this buffer should be at least @p max_size
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, char* value) noexcept
|
||||
{
|
||||
constexpr uint32_t num_bits = utility::bits_to_represent(MaxSize);
|
||||
|
||||
uint32_t length;
|
||||
BS_ASSERT(reader.serialize_bits(length, num_bits));
|
||||
|
||||
BS_ASSERT(length < MaxSize);
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
value[0] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
BS_ASSERT(reader.serialize_bytes(reinterpret_cast<uint8_t*>(value), length * 8));
|
||||
|
||||
value[length] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#pragma endregion
|
||||
|
||||
#ifdef __cpp_char8_t
|
||||
/**
|
||||
* @brief A trait used to serialize bounded c-style UTF-8 strings
|
||||
*/
|
||||
template<>
|
||||
struct serialize_traits<const char8_t*>
|
||||
{
|
||||
/**
|
||||
* @brief Writes a c-style UTF-8 string into the @p writer
|
||||
* @param writer The stream to write to
|
||||
* @param value The string to serialize
|
||||
* @param max_size The maximum expected length of the string, including the null terminator
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, const char8_t* value, uint32_t max_size) noexcept
|
||||
{
|
||||
uint32_t length = static_cast<uint32_t>(std::char_traits<char8_t>::length(value));
|
||||
|
||||
BS_ASSERT(length < max_size);
|
||||
|
||||
if (length == 0)
|
||||
return true;
|
||||
|
||||
uint32_t num_bits = utility::bits_to_represent(max_size);
|
||||
|
||||
BS_ASSERT(writer.serialize_bits(length, num_bits));
|
||||
|
||||
return writer.serialize_bytes(reinterpret_cast<const uint8_t*>(value), length * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read a c-style UTF-8 string from the @p reader into @p value
|
||||
* @param reader The stream to read from
|
||||
* @param value A pointer to the buffer that should be read into. The size of this buffer should be at least @p max_size
|
||||
* @param max_size The maximum expected length of the string, including the null terminator
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, char8_t* value, uint32_t max_size) noexcept
|
||||
{
|
||||
uint32_t num_bits = utility::bits_to_represent(max_size);
|
||||
|
||||
uint32_t length;
|
||||
BS_ASSERT(reader.serialize_bits(length, num_bits));
|
||||
|
||||
BS_ASSERT(length < max_size);
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
value[0] = '\0';
|
||||
return true;
|
||||
}
|
||||
|
||||
BS_ASSERT(reader.serialize_bytes(reinterpret_cast<uint8_t*>(value), length * 8));
|
||||
|
||||
value[length] = '\0';
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#endif
|
||||
|
||||
#pragma region std::basic_string
|
||||
/**
|
||||
* @brief A trait used to serialize any combination of std::basic_string
|
||||
* @tparam T The character type to use
|
||||
* @tparam Traits The trait type for the T type
|
||||
* @tparam Alloc The allocator to use
|
||||
*/
|
||||
template<typename T, typename Traits, typename Alloc>
|
||||
struct serialize_traits<std::basic_string<T, Traits, Alloc>>
|
||||
{
|
||||
/**
|
||||
* @brief Writes a string into the @p writer
|
||||
* @param writer The stream to write to
|
||||
* @param value The string to serialize
|
||||
* @param max_size The maximum expected length of the string, excluding the null terminator
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, in<std::basic_string<T, Traits, Alloc>> value, uint32_t max_size) noexcept
|
||||
{
|
||||
uint32_t length = static_cast<uint32_t>(value.size());
|
||||
|
||||
BS_ASSERT(length <= max_size);
|
||||
|
||||
uint32_t num_bits = utility::bits_to_represent(max_size);
|
||||
|
||||
BS_ASSERT(writer.serialize_bits(length, num_bits));
|
||||
|
||||
if (length == 0)
|
||||
return true;
|
||||
|
||||
return writer.serialize_bytes(reinterpret_cast<const uint8_t*>(value.c_str()), length * sizeof(T) * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a string from the @p reader into @p value
|
||||
* @param reader The stream to read from
|
||||
* @param value The string to read into. It will be resized if the read string won't fit
|
||||
* @param max_size The maximum expected length of the string, excluding the null terminator
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, out<std::basic_string<T, Traits, Alloc>> value, uint32_t max_size)
|
||||
{
|
||||
uint32_t num_bits = utility::bits_to_represent(max_size);
|
||||
|
||||
uint32_t length;
|
||||
BS_ASSERT(reader.serialize_bits(length, num_bits));
|
||||
|
||||
BS_ASSERT(length <= max_size);
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
value->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
value->resize(length);
|
||||
|
||||
BS_ASSERT(reader.serialize_bytes(reinterpret_cast<uint8_t*>(value->data()), length * sizeof(T) * 8));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A trait used to serialize any combination of std::basic_string with compiletime bounds
|
||||
* @tparam T The character type to use
|
||||
* @tparam Traits The trait type for the T type
|
||||
* @tparam Alloc The allocator to use
|
||||
* @tparam MaxSize The maximum expected length of the string, excluding the null terminator
|
||||
*/
|
||||
template<typename T, typename Traits, typename Alloc, size_t MaxSize>
|
||||
struct serialize_traits<bounded_string<std::basic_string<T, Traits, Alloc>, MaxSize>>
|
||||
{
|
||||
/**
|
||||
* @brief Writes a string into the @p writer
|
||||
* @param writer The stream to write to
|
||||
* @param value The string to serialize
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_writing_t<Stream>
|
||||
static serialize(Stream& writer, in<std::basic_string<T, Traits, Alloc>> value) noexcept
|
||||
{
|
||||
uint32_t length = static_cast<uint32_t>(value.size());
|
||||
|
||||
BS_ASSERT(length <= MaxSize);
|
||||
|
||||
constexpr uint32_t num_bits = utility::bits_to_represent(MaxSize);
|
||||
|
||||
BS_ASSERT(writer.serialize_bits(length, num_bits));
|
||||
|
||||
if (length == 0)
|
||||
return true;
|
||||
|
||||
return writer.serialize_bytes(reinterpret_cast<const uint8_t*>(value.c_str()), length * sizeof(T) * 8);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Reads a string from the @p reader into @p value
|
||||
* @param reader The stream to read from
|
||||
* @param value The string to read into. It will be resized if the read string won't fit
|
||||
* @return Success
|
||||
*/
|
||||
template<typename Stream>
|
||||
typename utility::is_reading_t<Stream>
|
||||
static serialize(Stream& reader, out<std::basic_string<T, Traits, Alloc>> value)
|
||||
{
|
||||
constexpr uint32_t num_bits = utility::bits_to_represent(MaxSize);
|
||||
|
||||
uint32_t length;
|
||||
BS_ASSERT(reader.serialize_bits(length, num_bits));
|
||||
|
||||
BS_ASSERT(length <= MaxSize);
|
||||
|
||||
if (length == 0)
|
||||
{
|
||||
value->clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
value->resize(length);
|
||||
|
||||
BS_ASSERT(reader.serialize_bytes(reinterpret_cast<uint8_t*>(value->data()), length * sizeof(T) * 8));
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
#pragma endregion
|
||||
}
|
||||
Reference in New Issue
Block a user