Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/bitstream/bitstream.h
Original file line number Diff line number Diff line change
@@ -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"
Expand Down
142 changes: 142 additions & 0 deletions include/bitstream/stream/bit_noop.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
#pragma once
#include "../utility/meta.h"

#include "multi.h"
#include "serialize_traits.h"
#include "stream_traits.h"

#include <cstdint>
#include <cstring>
#include <type_traits>

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<T>(...) 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<typename... Args, typename = std::enable_if_t<(utility::has_instance_serialize_v<Args, bit_noop> && ...)>>
[[nodiscard]] bool serialize(Args&&... args)
noexcept((noexcept(std::declval<Args&>().serialize(std::declval<bit_noop&>())) && ...))
{
return (std::forward<Args>(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<typename Trait, typename... Args, typename = utility::has_serialize_t<Trait, bit_noop, Args...>>
[[nodiscard]] bool serialize(Args&&... args) noexcept(utility::is_serialize_noexcept_v<Trait, bit_noop, Args...>)
{
return serialize_traits<Trait>::serialize(*this, std::forward<Args>(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<typename... Args, typename Trait, typename = utility::has_deduce_serialize_t<Trait, bit_noop, Args...>>
[[nodiscard]] bool serialize(Trait&& arg, Args&&... args) noexcept(utility::is_deduce_serialize_noexcept_v<Trait, bit_noop, Args...>)
{
return serialize_traits<utility::deduce_trait_t<Trait, bit_noop, Args...>>::serialize(*this, std::forward<Trait>(arg), std::forward<Args>(args)...);
}
};
}
19 changes: 16 additions & 3 deletions include/bitstream/stream/bit_reader.h
Original file line number Diff line number Diff line change
@@ -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 <cstdint>
#include <cstring>
#include <string>
#include <type_traits>

namespace bitstream
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -272,6 +271,20 @@ namespace bitstream
return true;
}

/**
* @brief Reads from the buffer into mulitple variables.
* @note Pass multi<T>(...) 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<typename... Args, typename = std::enable_if_t<(utility::has_instance_serialize_v<Args, bit_reader> && ...)>>
[[nodiscard]] bool serialize(Args&&... args)
noexcept((noexcept(std::declval<Args&>().serialize(std::declval<bit_reader&>())) && ...))
{
return (std::forward<Args>(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
Expand Down
18 changes: 16 additions & 2 deletions include/bitstream/stream/bit_writer.h
Original file line number Diff line number Diff line change
@@ -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"

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -316,6 +316,20 @@ namespace bitstream
return true;
}

/**
* @brief Writes to the buffer from multiple variables.
* @note Pass multi<T>(...) 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<typename... Args, typename = std::enable_if_t<(utility::has_instance_serialize_v<Args, bit_writer> && ...)>>
[[nodiscard]] bool serialize(Args&&... args)
noexcept((noexcept(std::declval<Args&>().serialize(std::declval<bit_writer&>())) && ...))
{
return (std::forward<Args>(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
Expand Down
36 changes: 36 additions & 0 deletions include/bitstream/stream/multi.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#pragma once
#include "../utility/meta.h"
#include "../utility/parameter.h"

#include "../stream/bit_noop.h"

#include "serialize_traits.h"

#include <tuple>

namespace bitstream
{
template<typename T, typename... Ts>
struct multi_args
{
std::tuple<Ts...> Args;

template<typename Stream, typename = utility::has_serialize_t<T, Stream, Ts...>>
[[nodiscard]] bool serialize(Stream& stream) noexcept(utility::is_serialize_noexcept_v<T, Stream, Ts...>)
{
return std::apply([&](auto&&... args) { return serialize_traits<T>::serialize(stream, args ...); }, std::move(Args));
}
};

template<typename T, typename... Args>
[[nodiscard]] multi_args<T, Args&&...> multi(Args&&... args) noexcept
{
return multi_args<T, Args&&...>{ std::forward_as_tuple(std::forward<Args>(args) ...) };
}

template<typename... Args, typename Trait, typename = utility::has_deduce_serialize_t<Trait, bit_noop, Args...>>
[[nodiscard]] multi_args<utility::deduce_trait_t<Trait, bit_noop, Args...>, Trait&&, Args&&...> multi(Trait&& arg, Args&&... args) noexcept
{
return multi_args<utility::deduce_trait_t<Trait, bit_noop, Args...>, Trait&&, Args&&...>{ std::forward_as_tuple(std::forward<Trait>(arg), std::forward<Args>(args) ...) };
}
}
6 changes: 2 additions & 4 deletions include/bitstream/traits/array_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,7 @@ namespace bitstream
* @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
static stream_writing_t<Stream> 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++)
Expand Down Expand Up @@ -144,8 +143,7 @@ namespace bitstream
* @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
static stream_reading_t<Stream> serialize(Stream& reader, T* values, int max_size, Args&&... args) noexcept
{
int prev_index = -1;
int index = 0;
Expand Down
12 changes: 4 additions & 8 deletions include/bitstream/traits/bool_trait.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,15 @@ namespace bitstream
struct serialize_traits<bool>
{
template<typename Stream>
typename utility::is_writing_t<Stream>
static serialize(Stream& writer, in<bool> value) noexcept
static stream_writing_t<Stream> 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
static stream_reading_t<Stream> serialize(Stream& reader, out<bool> value) noexcept
{
uint32_t unsigned_value;

Expand All @@ -43,8 +41,7 @@ namespace bitstream
struct serialize_traits<bool[Size]>
{
template<typename Stream>
typename utility::is_writing_t<Stream>
static serialize(Stream& writer, const bool* values) noexcept
static stream_writing_t<Stream> serialize(Stream& writer, const bool* values) noexcept
{
uint32_t unsigned_value;
for (size_t i = 0; i < Size; i++)
Expand All @@ -57,8 +54,7 @@ namespace bitstream
}

template<typename Stream>
typename utility::is_reading_t<Stream>
static serialize(Stream& reader, bool* values) noexcept
static stream_reading_t<Stream> serialize(Stream& reader, bool* values) noexcept
{
uint32_t unsigned_value;
for (size_t i = 0; i < Size; i++)
Expand Down
6 changes: 2 additions & 4 deletions include/bitstream/traits/checksum_trait.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,7 @@ namespace bitstream
constexpr static uint32_t protocol_size = sizeof(uint32_t);

template<typename Stream>
typename utility::is_writing_t<Stream>
static serialize(Stream& writer) noexcept
static stream_writing_t<Stream> serialize(Stream& writer) noexcept
{
if (writer.get_num_bits_serialized() == 0)
return writer.pad_to_size(4);
Expand All @@ -52,8 +51,7 @@ namespace bitstream
}

template<typename Stream>
typename utility::is_reading_t<Stream>
static serialize(Stream& reader) noexcept
static stream_reading_t<Stream> serialize(Stream& reader) noexcept
{
if (reader.get_num_bits_serialized() > 0)
return true;
Expand Down
Loading