From 2c637cc00e3d90a4553d5b854b87c600cb79cb68 Mon Sep 17 00:00:00 2001 From: KredeGC Date: Sat, 19 Jul 2025 22:18:06 +0200 Subject: [PATCH 1/4] Add multi serialize function --- include/bitstream/stream/bit_reader.h | 11 +++++-- include/bitstream/stream/bit_writer.h | 10 +++++-- include/bitstream/stream/multi.h | 42 ++++++++++++++++++++++++++ src/test/serialize_multi_test.cpp | 43 +++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 5 deletions(-) create mode 100644 include/bitstream/stream/multi.h create mode 100644 src/test/serialize_multi_test.cpp diff --git a/include/bitstream/stream/bit_reader.h b/include/bitstream/stream/bit_reader.h index ad385a1..7f84979 100644 --- a/include/bitstream/stream/bit_reader.h +++ b/include/bitstream/stream/bit_reader.h @@ -1,16 +1,14 @@ #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 @@ -272,6 +270,13 @@ namespace bitstream return true; } + 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..51e3e1f 100644 --- a/include/bitstream/stream/bit_writer.h +++ b/include/bitstream/stream/bit_writer.h @@ -1,15 +1,14 @@ #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 @@ -316,6 +315,13 @@ namespace bitstream return true; } + 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..ed18757 --- /dev/null +++ b/include/bitstream/stream/multi.h @@ -0,0 +1,42 @@ +#pragma once +#include "../utility/meta.h" +#include "../utility/parameter.h" + +#include "serialize_traits.h" + +#include + +namespace bitstream +{ + namespace utility + { + // Check if type 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; + } + + + template + struct multi_tuple + { + std::tuple Args; + + template> + 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 + multi_tuple multi(Args&&... args) noexcept + { + return multi_tuple{ std::forward_as_tuple(std::forward(args) ...) }; + } +} \ 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..c732efb --- /dev/null +++ b/src/test/serialize_multi_test.cpp @@ -0,0 +1,43 @@ +#include "../shared/assert.h" +#include "../shared/test.h" + +#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; + uint32_t in_value2 = 99; + + // Write some values + byte_buffer<16> buffer; + fixed_bit_writer writer(buffer); + + BS_TEST_ASSERT(writer.serialize( + multi(in_value1, 328, 611), + multi(in_value2, 11, 111) + )); + + uint32_t num_bits = writer.flush(); + + BS_TEST_ASSERT(num_bits == 16); + + // Read the values back and validate + uint32_t out_value1; + uint32_t out_value2; + fixed_bit_reader reader(buffer, num_bits); + + BS_TEST_ASSERT(reader.serialize( + multi(out_value1, 328U, 611U), + multi(out_value2, 11U, 111U) + )); + + BS_TEST_ASSERT(out_value1 == in_value1); + BS_TEST_ASSERT(out_value2 == in_value2); + } +} \ No newline at end of file From 5561d13922ce9572af71f3735f545bfb6be57330 Mon Sep 17 00:00:00 2001 From: KredeGC Date: Sat, 19 Jul 2025 22:19:46 +0200 Subject: [PATCH 2/4] Fix missing include --- include/bitstream/stream/bit_reader.h | 1 + include/bitstream/stream/bit_writer.h | 1 + 2 files changed, 2 insertions(+) diff --git a/include/bitstream/stream/bit_reader.h b/include/bitstream/stream/bit_reader.h index 7f84979..b7c51f9 100644 --- a/include/bitstream/stream/bit_reader.h +++ b/include/bitstream/stream/bit_reader.h @@ -9,6 +9,7 @@ #include "stream_traits.h" #include +#include #include namespace bitstream diff --git a/include/bitstream/stream/bit_writer.h b/include/bitstream/stream/bit_writer.h index 51e3e1f..fd65b86 100644 --- a/include/bitstream/stream/bit_writer.h +++ b/include/bitstream/stream/bit_writer.h @@ -9,6 +9,7 @@ #include "stream_traits.h" #include +#include #include #include From 4cf8d2e109c773a6c535a80038fb94f59770c4ef Mon Sep 17 00:00:00 2001 From: KredeGC Date: Sat, 19 Jul 2025 22:53:50 +0200 Subject: [PATCH 3/4] Work on serializaing multiple arguments --- include/bitstream/stream/bit_reader.h | 7 +++++++ include/bitstream/stream/bit_writer.h | 7 +++++++ include/bitstream/stream/multi.h | 20 +++----------------- include/bitstream/utility/meta.h | 11 +++++++++++ src/test/serialize_multi_test.cpp | 15 +++++++++------ 5 files changed, 37 insertions(+), 23 deletions(-) diff --git a/include/bitstream/stream/bit_reader.h b/include/bitstream/stream/bit_reader.h index b7c51f9..8c11412 100644 --- a/include/bitstream/stream/bit_reader.h +++ b/include/bitstream/stream/bit_reader.h @@ -271,6 +271,13 @@ 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())) && ...)) diff --git a/include/bitstream/stream/bit_writer.h b/include/bitstream/stream/bit_writer.h index fd65b86..72ae509 100644 --- a/include/bitstream/stream/bit_writer.h +++ b/include/bitstream/stream/bit_writer.h @@ -316,6 +316,13 @@ 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())) && ...)) diff --git a/include/bitstream/stream/multi.h b/include/bitstream/stream/multi.h index ed18757..2987f7b 100644 --- a/include/bitstream/stream/multi.h +++ b/include/bitstream/stream/multi.h @@ -8,22 +8,8 @@ namespace bitstream { - namespace utility - { - // Check if type 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; - } - - template - struct multi_tuple + struct multi_args { std::tuple Args; @@ -35,8 +21,8 @@ namespace bitstream }; template - multi_tuple multi(Args&&... args) noexcept + multi_args multi(Args&&... args) noexcept { - return multi_tuple{ std::forward_as_tuple(std::forward(args) ...) }; + return multi_args{ std::forward_as_tuple(std::forward(args) ...) }; } } \ No newline at end of file diff --git a/include/bitstream/utility/meta.h b/include/bitstream/utility/meta.h index 66c9ca4..dd1868f 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 {}; diff --git a/src/test/serialize_multi_test.cpp b/src/test/serialize_multi_test.cpp index c732efb..00a0e0d 100644 --- a/src/test/serialize_multi_test.cpp +++ b/src/test/serialize_multi_test.cpp @@ -5,6 +5,7 @@ #include #include +#include namespace bitstream::test::multi_serialize { @@ -12,7 +13,9 @@ namespace bitstream::test::multi_serialize { // Test serializing multiple values at once uint32_t in_value1 = 511; - uint32_t in_value2 = 99; + float in_value2 = 99.12345f; + + bounded_range range(-1000.0f, 1000.0f, 0.001f); // Write some values byte_buffer<16> buffer; @@ -20,24 +23,24 @@ namespace bitstream::test::multi_serialize BS_TEST_ASSERT(writer.serialize( multi(in_value1, 328, 611), - multi(in_value2, 11, 111) + multi(range, in_value2) )); uint32_t num_bits = writer.flush(); - BS_TEST_ASSERT(num_bits == 16); + BS_TEST_ASSERT(num_bits == 30); // Read the values back and validate uint32_t out_value1; - uint32_t out_value2; + float out_value2; fixed_bit_reader reader(buffer, num_bits); BS_TEST_ASSERT(reader.serialize( multi(out_value1, 328U, 611U), - multi(out_value2, 11U, 111U) + multi(range, out_value2) )); BS_TEST_ASSERT(out_value1 == in_value1); - BS_TEST_ASSERT(out_value2 == in_value2); + BS_TEST_ASSERT(std::abs(in_value2 - out_value2) <= range.get_precision()); } } \ No newline at end of file From 397fb803cd20ff71dfed7351f14c753274962206 Mon Sep 17 00:00:00 2001 From: KredeGC Date: Mon, 1 Dec 2025 23:58:42 +0100 Subject: [PATCH 4/4] Add multi function with deduced serialize trait Add likely and unlikely Add stream_writing_t and stream_reading_t --- include/bitstream/bitstream.h | 4 + include/bitstream/stream/bit_noop.h | 142 ++++++++++++++++++ include/bitstream/stream/bit_reader.h | 2 +- include/bitstream/stream/bit_writer.h | 2 +- include/bitstream/stream/multi.h | 12 +- include/bitstream/traits/array_traits.h | 6 +- include/bitstream/traits/bool_trait.h | 12 +- include/bitstream/traits/checksum_trait.h | 6 +- include/bitstream/traits/enum_trait.h | 12 +- include/bitstream/traits/float_trait.h | 12 +- include/bitstream/traits/integral_traits.h | 12 +- .../bitstream/traits/quantization_traits.h | 18 +-- include/bitstream/traits/string_traits.h | 30 ++-- include/bitstream/utility/assert.h | 6 +- include/bitstream/utility/likely.h | 9 ++ include/bitstream/utility/meta.h | 18 ++- src/test/serialize_multi_test.cpp | 35 +++++ 17 files changed, 252 insertions(+), 86 deletions(-) create mode 100644 include/bitstream/stream/bit_noop.h create mode 100644 include/bitstream/utility/likely.h 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 8c11412..86c3bc7 100644 --- a/include/bitstream/stream/bit_reader.h +++ b/include/bitstream/stream/bit_reader.h @@ -159,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); diff --git a/include/bitstream/stream/bit_writer.h b/include/bitstream/stream/bit_writer.h index 72ae509..f9a9491 100644 --- a/include/bitstream/stream/bit_writer.h +++ b/include/bitstream/stream/bit_writer.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); diff --git a/include/bitstream/stream/multi.h b/include/bitstream/stream/multi.h index 2987f7b..a2ff349 100644 --- a/include/bitstream/stream/multi.h +++ b/include/bitstream/stream/multi.h @@ -2,6 +2,8 @@ #include "../utility/meta.h" #include "../utility/parameter.h" +#include "../stream/bit_noop.h" + #include "serialize_traits.h" #include @@ -14,15 +16,21 @@ namespace bitstream std::tuple Args; template> - bool serialize(Stream& stream) noexcept(utility::is_serialize_noexcept_v) + [[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 - multi_args multi(Args&&... args) noexcept + [[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 dd1868f..abe96dd 100644 --- a/include/bitstream/utility/meta.h +++ b/include/bitstream/utility/meta.h @@ -31,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 {}; @@ -113,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 index 00a0e0d..276a5ce 100644 --- a/src/test/serialize_multi_test.cpp +++ b/src/test/serialize_multi_test.cpp @@ -43,4 +43,39 @@ namespace bitstream::test::multi_serialize 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