344 lines
9.7 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 <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
}