Attempting to use an external library to handle bitstreams. Isn't going the grreatest.

This commit is contained in:
2024-10-11 15:41:46 -04:00
parent b9502ebe86
commit 170fdddcf0
30 changed files with 3239 additions and 334 deletions
+165
View File
@@ -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;
}
};
}
+74
View File
@@ -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;
}
};
}
+81
View File
@@ -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;
}
};
}
+102
View File
@@ -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;
}
};
}
+233
View File
@@ -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;
}
};
}
+344
View File
@@ -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
}