#pragma once #include "../utility/assert.h" #include "../utility/bits.h" #include "../utility/meta.h" #include "../utility/parameter.h" #include "../stream/serialize_traits.h" #include #include namespace bitstream { /** * @brief Wrapper type for compiletime known string max_size */ template struct bounded_string; #pragma region const char* /** * @brief A trait used to serialize bounded c-style strings */ template<> struct serialize_traits { /** * @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 utility::is_writing_t static serialize(Stream& writer, const char* value, uint32_t max_size) noexcept { uint32_t length = static_cast(std::char_traits::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(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 utility::is_reading_t 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(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 struct serialize_traits> { /** * @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 utility::is_writing_t static serialize(Stream& writer, const char* value) noexcept { uint32_t length = static_cast(std::char_traits::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(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 utility::is_reading_t 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(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 { /** * @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 utility::is_writing_t static serialize(Stream& writer, const char8_t* value, uint32_t max_size) noexcept { uint32_t length = static_cast(std::char_traits::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(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 utility::is_reading_t 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(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 struct serialize_traits> { /** * @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 utility::is_writing_t static serialize(Stream& writer, in> value, uint32_t max_size) noexcept { uint32_t length = static_cast(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(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 utility::is_reading_t static serialize(Stream& reader, out> 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(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 struct serialize_traits, 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 utility::is_writing_t static serialize(Stream& writer, in> value) noexcept { uint32_t length = static_cast(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(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 utility::is_reading_t static serialize(Stream& reader, out> 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(value->data()), length * sizeof(T) * 8)); return true; } }; #pragma endregion }