diff --git a/include/bitstream/bitstream.h b/include/bitstream/bitstream.h index 264e28f..9f5e6b4 100644 --- a/include/bitstream/bitstream.h +++ b/include/bitstream/bitstream.h @@ -1,5 +1,9 @@ #pragma once +// Possible defines +// #define BS_DEBUG_BREAK // Stops execution when serialization fails (debug break, trap etc.) +// #define BS_EQUAL_PATH // Stops optimization of the happy path + // Quantization #include "quantization/bounded_range.h" #include "quantization/half_precision.h" diff --git a/include/bitstream/stream/bit_noop.h b/include/bitstream/stream/bit_noop.h new file mode 100644 index 0000000..d8e8296 --- /dev/null +++ b/include/bitstream/stream/bit_noop.h @@ -0,0 +1,142 @@ +#pragma once +#include "../utility/meta.h" + +#include "multi.h" +#include "serialize_traits.h" +#include "stream_traits.h" + +#include +#include +#include + +namespace bitstream +{ + /** + * @brief A noop stream + */ + class bit_noop + { + public: + static constexpr bool writing = true; + static constexpr bool reading = false; + + /** + * @brief Returns the buffer that this reader is currently serializing from + * @return The buffer + */ + [[nodiscard]] const uint8_t* get_buffer() const noexcept { return nullptr; } + + /** + * @brief Returns the number of bits which have been read from the buffer + * @return The number of bits which have been read + */ + [[nodiscard]] uint32_t get_num_bits_serialized() const noexcept { return 0; } + + /** + * @brief Returns the number of bytes which have been read from the buffer + * @return The number of bytes which have been read + */ + [[nodiscard]] uint32_t get_num_bytes_serialized() const noexcept { return 0; } + + /** + * @brief Returns whether the @p num_bits be read from the buffer + * @param num_bits The number of bits to test + * @return Whether the number of bits can be read from the buffer + */ + [[nodiscard]] bool can_serialize_bits(uint32_t num_bits) const noexcept { return false; } + + /** + * @brief Returns the number of bits which have not been read yet + * @note The same as get_total_bits() - get_num_bits_serialized() + * @return The remaining space in the buffer + */ + [[nodiscard]] uint32_t get_remaining_bits() const noexcept { return 0; } + + /** + * @brief Returns the size of the buffer, in bits + * @return The size of the buffer, in bits + */ + [[nodiscard]] uint32_t get_total_bits() const noexcept { return 0; } + + /** + * @brief Pads the buffer up to the given number of bytes + * @param num_bytes The byte number to pad to + * @return Returns false if the current size of the buffer is bigger than @p num_bytes or if the padded bits are not zeros. + */ + [[nodiscard]] bool pad_to_size(uint32_t num_bytes) noexcept { return false; } + + /** + * @brief Pads the buffer up with the given number of bytes + * @param num_bytes The amount of bytes to pad + * @return Returns false if the current size of the buffer is bigger than @p num_bytes or if the padded bits are not zeros. + */ + [[nodiscard]] bool pad(uint32_t num_bytes) noexcept { return false; } + + /** + * @brief Pads the buffer with up to 8 zeros, so that the next read is byte-aligned + * @notes Return false if the padded bits are not zeros + * @return Returns false if the padded bits are not zeros + */ + [[nodiscard]] bool align() noexcept { return false; } + + /** + * @brief Reads the first @p num_bits bits of @p value from the buffer + * @param value The value to serialize + * @param num_bits The number of bits of the @p value to serialize + * @return Returns false if @p num_bits is less than 1 or greater than 32 or if reading the given number of bits would overflow the buffer + */ + [[nodiscard]] bool serialize_bits(uint32_t& value, uint32_t num_bits) noexcept { return false; } + + /** + * @brief Reads the first @p num_bits bits of the given byte array, 32 bits at a time + * @param bytes The bytes to serialize + * @param num_bits The number of bits of the @p bytes to serialize + * @return Returns false if @p num_bits is less than 1 or if reading the given number of bits would overflow the buffer + */ + [[nodiscard]] bool serialize_bytes(uint8_t* bytes, uint32_t num_bits) noexcept { return false; } + + /** + * @brief Reads from the buffer into mulitple variables. + * @note Pass multi(...) to this in place of multiple calls to the regular serialize functions. + * @tparam ...Args The types of the arguments to pass to the serialize function + * @param ...args The arguments to pass to the serialize function + * @return Whether successful or not + */ + template && ...)>> + [[nodiscard]] bool serialize(Args&&... args) + noexcept((noexcept(std::declval().serialize(std::declval())) && ...)) + { + return (std::forward(args).serialize(*this) && ...); + } + + /** + * @brief Reads from the buffer, using the given @p Trait. + * @note The Trait type in this function must always be explicitly declared + * @tparam Trait A template specialization of serialize_trait<> + * @tparam ...Args The types of the arguments to pass to the serialize function + * @param ...args The arguments to pass to the serialize function + * @return Whether successful or not + */ + template> + [[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_serialize_noexcept_v) + { + return serialize_traits::serialize(*this, std::forward(args)...); + } + + /** + * @brief Reads from the buffer, by trying to deduce the trait. + * @note The Trait type in this function is always implicit and will be deduced from the first argument if possible. + * If the trait cannot be deduced it will not compile. + * @tparam Trait The type of the first argument, which will be used to deduce the trait specialization + * @tparam ...Args The types of the arguments to pass to the serialize function + * @param arg The first argument to pass to the serialize function + * @param ...args The rest of the arguments to pass to the serialize function + * @return Whether successful or not + */ + template> + [[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_deduce_serialize_noexcept_v) + { + return serialize_traits>::serialize(*this, std::forward(arg), std::forward(args)...); + } + }; +} \ No newline at end of file diff --git a/include/bitstream/stream/bit_reader.h b/include/bitstream/stream/bit_reader.h index ad385a1..86c3bc7 100644 --- a/include/bitstream/stream/bit_reader.h +++ b/include/bitstream/stream/bit_reader.h @@ -1,16 +1,15 @@ #pragma once #include "../utility/assert.h" -#include "../utility/crc.h" #include "../utility/endian.h" #include "../utility/meta.h" #include "byte_buffer.h" +#include "multi.h" #include "serialize_traits.h" #include "stream_traits.h" #include #include -#include #include namespace bitstream @@ -160,7 +159,7 @@ namespace bitstream [[nodiscard]] bool align() noexcept { uint32_t remainder = get_num_bits_serialized() % 8U; - if (remainder != 0U) + if (remainder != 0U) BS_LIKELY { uint32_t zero; bool status = serialize_bits(zero, 8U - remainder); @@ -272,6 +271,20 @@ namespace bitstream return true; } + /** + * @brief Reads from the buffer into mulitple variables. + * @note Pass multi(...) to this in place of multiple calls to the regular serialize functions. + * @tparam ...Args The types of the arguments to pass to the serialize function + * @param ...args The arguments to pass to the serialize function + * @return Whether successful or not + */ + template && ...)>> + [[nodiscard]] bool serialize(Args&&... args) + noexcept((noexcept(std::declval().serialize(std::declval())) && ...)) + { + return (std::forward(args).serialize(*this) && ...); + } + /** * @brief Reads from the buffer, using the given @p Trait. * @note The Trait type in this function must always be explicitly declared diff --git a/include/bitstream/stream/bit_writer.h b/include/bitstream/stream/bit_writer.h index 04585b8..f9a9491 100644 --- a/include/bitstream/stream/bit_writer.h +++ b/include/bitstream/stream/bit_writer.h @@ -1,10 +1,10 @@ #pragma once #include "../utility/assert.h" -#include "../utility/crc.h" #include "../utility/endian.h" #include "../utility/meta.h" #include "byte_buffer.h" +#include "multi.h" #include "serialize_traits.h" #include "stream_traits.h" @@ -186,7 +186,7 @@ namespace bitstream [[nodiscard]] bool align() noexcept { uint32_t remainder = m_ScratchBits % 8U; - if (remainder != 0U) + if (remainder != 0U) BS_LIKELY { uint32_t zero = 0U; bool status = serialize_bits(zero, 8U - remainder); @@ -316,6 +316,20 @@ namespace bitstream return true; } + /** + * @brief Writes to the buffer from multiple variables. + * @note Pass multi(...) to this in place of multiple calls to the regular serialize functions. + * @tparam ...Args The types of the arguments to pass to the serialize function + * @param ...args The arguments to pass to the serialize function + * @return Whether successful or not + */ + template && ...)>> + [[nodiscard]] bool serialize(Args&&... args) + noexcept((noexcept(std::declval().serialize(std::declval())) && ...)) + { + return (std::forward(args).serialize(*this) && ...); + } + /** * @brief Writes to the buffer, using the given @p Trait. * @note The Trait type in this function must always be explicitly declared diff --git a/include/bitstream/stream/multi.h b/include/bitstream/stream/multi.h new file mode 100644 index 0000000..a2ff349 --- /dev/null +++ b/include/bitstream/stream/multi.h @@ -0,0 +1,36 @@ +#pragma once +#include "../utility/meta.h" +#include "../utility/parameter.h" + +#include "../stream/bit_noop.h" + +#include "serialize_traits.h" + +#include + +namespace bitstream +{ + template + struct multi_args + { + std::tuple Args; + + template> + [[nodiscard]] bool serialize(Stream& stream) noexcept(utility::is_serialize_noexcept_v) + { + return std::apply([&](auto&&... args) { return serialize_traits::serialize(stream, args ...); }, std::move(Args)); + } + }; + + template + [[nodiscard]] multi_args multi(Args&&... args) noexcept + { + return multi_args{ std::forward_as_tuple(std::forward(args) ...) }; + } + + template> + [[nodiscard]] multi_args, Trait&&, Args&&...> multi(Trait&& arg, Args&&... args) noexcept + { + return multi_args, Trait&&, Args&&...>{ std::forward_as_tuple(std::forward(arg), std::forward(args) ...) }; + } +} \ No newline at end of file diff --git a/include/bitstream/traits/array_traits.h b/include/bitstream/traits/array_traits.h index 829fa67..73bef24 100644 --- a/include/bitstream/traits/array_traits.h +++ b/include/bitstream/traits/array_traits.h @@ -114,8 +114,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, T* values, int max_size, Compare compare, Args&&... args) noexcept + static stream_writing_t 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++) @@ -144,8 +143,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, T* values, int max_size, Args&&... args) noexcept + static stream_reading_t serialize(Stream& reader, T* values, int max_size, Args&&... args) noexcept { int prev_index = -1; int index = 0; diff --git a/include/bitstream/traits/bool_trait.h b/include/bitstream/traits/bool_trait.h index 0d4b8fe..cc1ad78 100644 --- a/include/bitstream/traits/bool_trait.h +++ b/include/bitstream/traits/bool_trait.h @@ -14,8 +14,7 @@ namespace bitstream struct serialize_traits { template - typename utility::is_writing_t - static serialize(Stream& writer, in value) noexcept + static stream_writing_t serialize(Stream& writer, in value) noexcept { uint32_t unsigned_value = value; @@ -23,8 +22,7 @@ namespace bitstream } template - typename utility::is_reading_t - static serialize(Stream& reader, out value) noexcept + static stream_reading_t serialize(Stream& reader, out value) noexcept { uint32_t unsigned_value; @@ -43,8 +41,7 @@ namespace bitstream struct serialize_traits { template - typename utility::is_writing_t - static serialize(Stream& writer, const bool* values) noexcept + static stream_writing_t serialize(Stream& writer, const bool* values) noexcept { uint32_t unsigned_value; for (size_t i = 0; i < Size; i++) @@ -57,8 +54,7 @@ namespace bitstream } template - typename utility::is_reading_t - static serialize(Stream& reader, bool* values) noexcept + static stream_reading_t serialize(Stream& reader, bool* values) noexcept { uint32_t unsigned_value; for (size_t i = 0; i < Size; i++) diff --git a/include/bitstream/traits/checksum_trait.h b/include/bitstream/traits/checksum_trait.h index 1a6282e..8490671 100644 --- a/include/bitstream/traits/checksum_trait.h +++ b/include/bitstream/traits/checksum_trait.h @@ -27,8 +27,7 @@ namespace bitstream constexpr static uint32_t protocol_size = sizeof(uint32_t); template - typename utility::is_writing_t - static serialize(Stream& writer) noexcept + static stream_writing_t serialize(Stream& writer) noexcept { if (writer.get_num_bits_serialized() == 0) return writer.pad_to_size(4); @@ -52,8 +51,7 @@ namespace bitstream } template - typename utility::is_reading_t - static serialize(Stream& reader) noexcept + static stream_reading_t serialize(Stream& reader) noexcept { if (reader.get_num_bits_serialized() > 0) return true; diff --git a/include/bitstream/traits/enum_trait.h b/include/bitstream/traits/enum_trait.h index 342aa3b..b3d63ba 100644 --- a/include/bitstream/traits/enum_trait.h +++ b/include/bitstream/traits/enum_trait.h @@ -25,8 +25,7 @@ namespace bitstream using value_type = std::underlying_type_t; template - typename utility::is_writing_t - static serialize(Stream& writer, T value, value_type min = 0, value_type max = (std::numeric_limits::max)()) noexcept + static stream_writing_t serialize(Stream& writer, T value, value_type min = 0, value_type max = (std::numeric_limits::max)()) noexcept { value_type unsigned_value = static_cast(value); @@ -34,8 +33,7 @@ namespace bitstream } template - typename utility::is_reading_t - static serialize(Stream& reader, T& value, value_type min = 0, value_type max = (std::numeric_limits::max)()) noexcept + static stream_reading_t serialize(Stream& reader, T& value, value_type min = 0, value_type max = (std::numeric_limits::max)()) noexcept { value_type unsigned_value; @@ -57,8 +55,7 @@ namespace bitstream using bound_type = bounded_int; template - typename utility::is_writing_t - static serialize(Stream& writer, T value) noexcept + static stream_writing_t serialize(Stream& writer, T value) noexcept { value_type unsigned_value = static_cast(value); @@ -66,8 +63,7 @@ namespace bitstream } template - typename utility::is_reading_t - static serialize(Stream& reader, T& value) noexcept + static stream_reading_t serialize(Stream& reader, T& value) noexcept { value_type unsigned_value; diff --git a/include/bitstream/traits/float_trait.h b/include/bitstream/traits/float_trait.h index a1f1641..052490a 100644 --- a/include/bitstream/traits/float_trait.h +++ b/include/bitstream/traits/float_trait.h @@ -23,8 +23,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, in value) noexcept + static stream_writing_t serialize(Stream& writer, in value) noexcept { uint32_t tmp; std::memcpy(&tmp, &value, sizeof(float)); @@ -41,8 +40,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, float& value) noexcept + static stream_reading_t serialize(Stream& reader, float& value) noexcept { uint32_t tmp; @@ -67,8 +65,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, in value) noexcept + static stream_writing_t serialize(Stream& writer, in value) noexcept { uint32_t tmp[2]; std::memcpy(tmp, &value, sizeof(double)); @@ -86,8 +83,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, double& value) noexcept + static stream_reading_t serialize(Stream& reader, double& value) noexcept { uint32_t tmp[2]; diff --git a/include/bitstream/traits/integral_traits.h b/include/bitstream/traits/integral_traits.h index d6653fe..5361dc2 100644 --- a/include/bitstream/traits/integral_traits.h +++ b/include/bitstream/traits/integral_traits.h @@ -38,8 +38,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, in value) noexcept + static stream_writing_t serialize(Stream& writer, in value) noexcept { static_assert(Min < Max); @@ -75,8 +74,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, T& value) noexcept + static stream_reading_t serialize(Stream& reader, T& value) noexcept { static_assert(Min < Max); @@ -133,8 +131,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, in value, T min, T max) noexcept + static stream_writing_t serialize(Stream& writer, in value, T min, T max) noexcept { BS_ASSERT(min < max); @@ -175,8 +172,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, T& value, T min, T max) noexcept + static stream_reading_t serialize(Stream& reader, T& value, T min, T max) noexcept { BS_ASSERT(min < max); diff --git a/include/bitstream/traits/quantization_traits.h b/include/bitstream/traits/quantization_traits.h index 5d035d1..f41a325 100644 --- a/include/bitstream/traits/quantization_traits.h +++ b/include/bitstream/traits/quantization_traits.h @@ -19,8 +19,7 @@ namespace bitstream struct serialize_traits { template - typename utility::is_writing_t - static serialize(Stream& stream, in value) noexcept + static stream_writing_t serialize(Stream& stream, in value) noexcept { uint32_t int_value = half_precision::quantize(value); @@ -30,8 +29,7 @@ namespace bitstream } template - typename utility::is_reading_t - static serialize(Stream& stream, out value) noexcept + static stream_reading_t serialize(Stream& stream, out value) noexcept { uint32_t int_value; @@ -50,8 +48,7 @@ namespace bitstream struct serialize_traits { template - typename utility::is_writing_t - static serialize(Stream& stream, in range, in value) noexcept + static stream_writing_t serialize(Stream& stream, in range, in value) noexcept { uint32_t int_value = range.quantize(value); @@ -61,8 +58,7 @@ namespace bitstream } template - typename utility::is_reading_t - static serialize(Stream& stream, in range, out value) noexcept + static stream_reading_t serialize(Stream& stream, in range, out value) noexcept { uint32_t int_value; @@ -81,8 +77,7 @@ namespace bitstream struct serialize_traits> { template - typename utility::is_writing_t - static serialize(Stream& stream, in value) noexcept + static stream_writing_t serialize(Stream& stream, in value) noexcept { quantized_quaternion quantized_quat = smallest_three::quantize(value); @@ -95,8 +90,7 @@ namespace bitstream } template - typename utility::is_reading_t - static serialize(Stream& stream, out value) noexcept + static stream_reading_t serialize(Stream& stream, out value) noexcept { quantized_quaternion quantized_quat; diff --git a/include/bitstream/traits/string_traits.h b/include/bitstream/traits/string_traits.h index 3c7b14c..9a00491 100644 --- a/include/bitstream/traits/string_traits.h +++ b/include/bitstream/traits/string_traits.h @@ -32,8 +32,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, const char* value, uint32_t max_size) noexcept + static stream_writing_t serialize(Stream& writer, const char* value, uint32_t max_size) noexcept { uint32_t length = static_cast(std::char_traits::length(value)); @@ -57,8 +56,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, char* value, uint32_t max_size) noexcept + static stream_reading_t serialize(Stream& reader, char* value, uint32_t max_size) noexcept { uint32_t num_bits = utility::bits_to_represent(max_size); @@ -95,8 +93,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, const char* value) noexcept + static stream_writing_t serialize(Stream& writer, const char* value) noexcept { uint32_t length = static_cast(std::char_traits::length(value)); @@ -119,8 +116,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, char* value) noexcept + static stream_reading_t serialize(Stream& reader, char* value) noexcept { constexpr uint32_t num_bits = utility::bits_to_represent(MaxSize); @@ -159,8 +155,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, const char8_t* value, uint32_t max_size) noexcept + static stream_writing_t serialize(Stream& writer, const char8_t* value, uint32_t max_size) noexcept { uint32_t length = static_cast(std::char_traits::length(value)); @@ -184,8 +179,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, char8_t* value, uint32_t max_size) noexcept + static stream_reading_t serialize(Stream& reader, char8_t* value, uint32_t max_size) noexcept { uint32_t num_bits = utility::bits_to_represent(max_size); @@ -227,8 +221,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, in> value, uint32_t max_size) noexcept + static stream_writing_t serialize(Stream& writer, in> value, uint32_t max_size) noexcept { uint32_t length = static_cast(value.size()); @@ -252,8 +245,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, out> value, uint32_t max_size) + static stream_reading_t serialize(Stream& reader, out> value, uint32_t max_size) { uint32_t num_bits = utility::bits_to_represent(max_size); @@ -293,8 +285,7 @@ namespace bitstream * @return Success */ template - typename utility::is_writing_t - static serialize(Stream& writer, in> value) noexcept + static stream_writing_t serialize(Stream& writer, in> value) noexcept { uint32_t length = static_cast(value.size()); @@ -317,8 +308,7 @@ namespace bitstream * @return Success */ template - typename utility::is_reading_t - static serialize(Stream& reader, out> value) + static stream_reading_t serialize(Stream& reader, out> value) { constexpr uint32_t num_bits = utility::bits_to_represent(MaxSize); diff --git a/include/bitstream/utility/assert.h b/include/bitstream/utility/assert.h index a02e6ef..866fa33 100644 --- a/include/bitstream/utility/assert.h +++ b/include/bitstream/utility/assert.h @@ -1,5 +1,7 @@ #pragma once +#include "likely.h" + #ifdef BS_DEBUG_BREAK #if defined(_WIN32) // Windows #define BS_BREAKPOINT() __debugbreak() @@ -10,9 +12,9 @@ #define BS_BREAKPOINT() throw #endif -#define BS_ASSERT(...) if (!(__VA_ARGS__)) { BS_BREAKPOINT(); return false; } +#define BS_ASSERT(...) if (!(__VA_ARGS__)) BS_UNLIKELY { BS_BREAKPOINT(); return false; } #else // BS_DEBUG_BREAK -#define BS_ASSERT(...) if (!(__VA_ARGS__)) { return false; } +#define BS_ASSERT(...) if (!(__VA_ARGS__)) BS_UNLIKELY { return false; } #define BS_BREAKPOINT() throw #endif // BS_DEBUG_BREAK \ No newline at end of file diff --git a/include/bitstream/utility/likely.h b/include/bitstream/utility/likely.h new file mode 100644 index 0000000..6a0199b --- /dev/null +++ b/include/bitstream/utility/likely.h @@ -0,0 +1,9 @@ +#pragma once + +#if !defined(BS_EQUAL_PATH) && defined (__has_cpp_attribute) && __has_cpp_attribute(likely) >= 201803L +# define BS_LIKELY [[likely]] +# define BS_UNLIKELY [[unlikely]] +#else +# define BS_LIKELY +# define BS_UNLIKELY +#endif // __has_cpp_attribute(likely) \ No newline at end of file diff --git a/include/bitstream/utility/meta.h b/include/bitstream/utility/meta.h index 66c9ca4..abe96dd 100644 --- a/include/bitstream/utility/meta.h +++ b/include/bitstream/utility/meta.h @@ -6,6 +6,17 @@ namespace bitstream::utility { + // Check if instance has a serializable trait + template + struct has_instance_serialize : std::false_type {}; + + template + struct has_instance_serialize().serialize(std::declval()))>> : std::true_type {}; + + template + constexpr bool has_instance_serialize_v = has_instance_serialize::value; + + // Check if type has a serializable trait template struct has_serialize : std::false_type {}; @@ -20,14 +31,6 @@ namespace bitstream::utility constexpr bool has_serialize_v = has_serialize::value; - // Check if stream is writing or reading - template - using is_writing_t = std::enable_if_t; - - template - using is_reading_t = std::enable_if_t; - - // Check if type is noexcept, if it exists template struct is_serialize_noexcept : std::false_type {}; @@ -102,4 +105,14 @@ namespace bitstream::utility template constexpr bool is_deduce_serialize_noexcept_v = is_serialize_noexcept_v, Stream, Trait, Args...>; +} + +namespace bitstream +{ + // Check if stream is writing or reading + template + using stream_writing_t = std::enable_if_t; + + template + using stream_reading_t = std::enable_if_t; } \ No newline at end of file diff --git a/src/test/serialize_multi_test.cpp b/src/test/serialize_multi_test.cpp new file mode 100644 index 0000000..276a5ce --- /dev/null +++ b/src/test/serialize_multi_test.cpp @@ -0,0 +1,81 @@ +#include "../shared/assert.h" +#include "../shared/test.h" + +#include +#include + +#include +#include + +namespace bitstream::test::multi_serialize +{ + BS_ADD_TEST(test_serialize_multi) + { + // Test serializing multiple values at once + uint32_t in_value1 = 511; + float in_value2 = 99.12345f; + + bounded_range range(-1000.0f, 1000.0f, 0.001f); + + // Write some values + byte_buffer<16> buffer; + fixed_bit_writer writer(buffer); + + BS_TEST_ASSERT(writer.serialize( + multi(in_value1, 328, 611), + multi(range, in_value2) + )); + + uint32_t num_bits = writer.flush(); + + BS_TEST_ASSERT(num_bits == 30); + + // Read the values back and validate + uint32_t out_value1; + float out_value2; + fixed_bit_reader reader(buffer, num_bits); + + BS_TEST_ASSERT(reader.serialize( + multi(out_value1, 328U, 611U), + multi(range, out_value2) + )); + + BS_TEST_ASSERT(out_value1 == in_value1); + BS_TEST_ASSERT(std::abs(in_value2 - out_value2) <= range.get_precision()); + } + + BS_ADD_TEST(test_serialize_multi_deduce) + { + // Test serializing multiple values at once + uint32_t in_value1 = 511; + float in_value2 = 99.12345f; + + bounded_range range(-1000.0f, 1000.0f, 0.001f); + + // Write some values + byte_buffer<16> buffer; + fixed_bit_writer writer(buffer); + + BS_TEST_ASSERT(writer.serialize( + multi(in_value1, 328, 611), + multi(range, in_value2) + )); + + uint32_t num_bits = writer.flush(); + + BS_TEST_ASSERT(num_bits == 30); + + // Read the values back and validate + uint32_t out_value1; + float out_value2; + fixed_bit_reader reader(buffer, num_bits); + + BS_TEST_ASSERT(reader.serialize( + multi(out_value1, 328U, 611U), + multi(range, out_value2) + )); + + BS_TEST_ASSERT(out_value1 == in_value1); + BS_TEST_ASSERT(std::abs(in_value2 - out_value2) <= range.get_precision()); + } +} \ No newline at end of file