Skip to content

Commit

Permalink
Merge pull request #5 from KredeGC/dev
Browse files Browse the repository at this point in the history
Add variable length encoding to array_traits
  • Loading branch information
KredeGC authored Feb 6, 2023
2 parents baf6091 + 3e428db commit 7206d9f
Show file tree
Hide file tree
Showing 5 changed files with 242 additions and 70 deletions.
118 changes: 92 additions & 26 deletions include/bitstream/traits/array_traits.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#pragma once
#include "../utility/assert.h"
#include "../utility/bits.h"

#include "../stream/serialize_traits.h"
#include "../stream/bit_reader.h"
#include "../stream/bit_writer.h"

#include "../traits/bool_trait.h"
#include "../traits/integral_traits.h"

#include <cstdint>

namespace bitstream
Expand All @@ -16,49 +18,113 @@ namespace bitstream
template<typename T, typename Trait>
struct serialize_traits<array_subset<T, Trait>>
{
private:
template<uint32_t Min, uint32_t Max, typename Stream>
static bool serialize_difference(Stream& stream, int& previous, int& current, uint32_t& difference)
{
bool use_bits;
if (Stream::writing)
use_bits = difference <= Max;
BS_ASSERT(stream.template serialize<bool>(use_bits));
if (use_bits)
{
using bounded_trait = bounded_int<uint32_t, Min, Max>;

BS_ASSERT(stream.template serialize<bounded_trait>(difference));
if (Stream::reading)
current = previous + difference;
previous = current;
return true;
}

return false;
}

template<typename Stream>
static bool serialize_index(Stream& stream, int& previous, int& current, int max_size)
{
uint32_t difference;
if (Stream::writing)
{
BS_ASSERT(previous < current);
difference = current - previous;
BS_ASSERT(difference > 0);
}

// +1 (1 bit)
bool plus_one;
if (Stream::writing)
plus_one = difference == 1;
BS_ASSERT(stream.template serialize<bool>(plus_one));
if (plus_one)
{
if (Stream::reading)
current = previous + 1;
previous = current;
return true;
}

// [+2,5] -> [0,3] (2 bits)
if (serialize_difference<2, 5>(stream, previous, current, difference))
return true;

// [6,13] -> [0,7] (3 bits)
if (serialize_difference<6, 13>(stream, previous, current, difference))
return true;

// [14,29] -> [0,15] (4 bits)
if (serialize_difference<14, 29>(stream, previous, current, difference))
return true;

// [30,61] -> [0,31] (5 bits)
if (serialize_difference<30, 61>(stream, previous, current, difference))
return true;

// [62,125] -> [0,63] (6 bits)
if (serialize_difference<62, 125>(stream, previous, current, difference))
return true;

// [126,MaxObjects+1]
BS_ASSERT(stream.template serialize<uint32_t>(difference, 126, max_size));
if (Stream::reading)
current = previous + difference;
previous = current;
return true;
}

public:
template<typename Compare, typename... Args>
static bool serialize(bit_writer& writer, T* values, uint32_t max_size, Compare compare, Args&&... args) noexcept
static bool serialize(bit_writer& writer, T* values, int max_size, Compare compare, Args&&... args) noexcept
{
uint32_t prev_index = 0;
for (uint32_t i = 0; i < max_size; i++)
int prev_index = -1;
for (int index = 0; index < max_size; index++)
{
if (!compare(values[i]))
if (!compare(values[index]))
continue;

uint32_t num_bits = utility::bits_in_range(prev_index, max_size);

uint32_t index = i - prev_index;
BS_ASSERT(writer.serialize_bits(index, num_bits));

BS_ASSERT(writer.serialize<Trait>(values[i], std::forward<Args>(args)...));
BS_ASSERT(serialize_index(writer, prev_index, index, max_size));

prev_index = i;
BS_ASSERT(writer.serialize<Trait>(values[index], std::forward<Args>(args)...));
}

uint32_t num_bits = utility::bits_in_range(prev_index, max_size);

BS_ASSERT(writer.serialize_bits(max_size - prev_index, num_bits));
BS_ASSERT(serialize_index(writer, prev_index, max_size, max_size));

return true;
}

template<typename Compare, typename... Args>
static bool serialize(bit_reader& reader, T* values, uint32_t max_size, Compare compare, Args&&... args) noexcept
static bool serialize(bit_reader& reader, T* values, int max_size, Compare compare, Args&&... args) noexcept
{
uint32_t prev_index = 0;
for (uint32_t i = 0; i <= max_size; i++)
int prev_index = -1;
int index = 0;
while (true)
{
uint32_t num_bits = utility::bits_in_range(prev_index, max_size);

uint32_t index;
BS_ASSERT(reader.serialize_bits(index, num_bits));
BS_ASSERT(serialize_index(reader, prev_index, index, max_size));

if (index + prev_index == max_size)
if (index == max_size)
break;

BS_ASSERT(reader.serialize<Trait>(values[index + prev_index], std::forward<Args>(args)...));

prev_index += index;
BS_ASSERT(reader.serialize<Trait>(values[index], std::forward<Args>(args)...));
}

return true;
Expand Down
34 changes: 34 additions & 0 deletions include/bitstream/traits/bool_trait.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
#pragma once
#include "../utility/assert.h"

#include "../stream/serialize_traits.h"
#include "../stream/bit_reader.h"
#include "../stream/bit_writer.h"

namespace bitstream
{
/**
* @brief A trait used to serialize a boolean as a single bit
*/
template<>
struct serialize_traits<bool>
{
static bool serialize(bit_writer& writer, bool value) noexcept
{
uint32_t unsigned_value = value;

return writer.serialize_bits(unsigned_value, 1U);
}

static bool serialize(bit_reader& reader, bool& value) noexcept
{
uint32_t unsigned_value;

BS_ASSERT(reader.serialize_bits(unsigned_value, 1U));

value = unsigned_value;

return true;
}
};
}
80 changes: 44 additions & 36 deletions include/bitstream/traits/integral_traits.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,67 +27,75 @@ namespace bitstream
template<typename T>
struct serialize_traits<T, typename std::enable_if_t<std::is_integral_v<T>>>
{
static bool serialize(bit_writer& writer, const T& value, T min = (std::numeric_limits<T>::min)(), T max = (std::numeric_limits<T>::max)()) noexcept
static bool serialize(bit_writer& writer, T value, T min = (std::numeric_limits<T>::min)(), T max = (std::numeric_limits<T>::max)()) noexcept
{
BS_ASSERT(min < max);

BS_ASSERT(value >= min && value < max);
BS_ASSERT(value >= min && value <= max);

int num_bits = static_cast<int>(utility::bits_in_range(min, max));
uint32_t num_bits = utility::bits_in_range(min, max);

BS_ASSERT(num_bits <= sizeof(T) * 8);

if constexpr (sizeof(T) > 4)
{
// If the given range is bigger than a word (32 bits)
uint32_t unsigned_value = static_cast<uint32_t>(value - min);
BS_ASSERT(writer.serialize_bits(unsigned_value, 32));
if (num_bits > 32)
{
// If the given range is bigger than a word (32 bits)
uint32_t unsigned_value = static_cast<uint32_t>(value - min);
BS_ASSERT(writer.serialize_bits(unsigned_value, 32));

unsigned_value = static_cast<uint32_t>((value - min) >> 32);
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits - 32));
}
else
{
// If the given range is smaller than or equal to a word (32 bits)
uint32_t unsigned_value = static_cast<uint32_t>(value - min);
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits));
unsigned_value = static_cast<uint32_t>((value - min) >> 32);
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits - 32));

return true;
}
}

// If the given range is smaller than or equal to a word (32 bits)
uint32_t unsigned_value = static_cast<uint32_t>(value - min);
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits));

return true;
}

static bool serialize(bit_reader& reader, T& value, T min = (std::numeric_limits<T>::min)(), T max = (std::numeric_limits<T>::max)()) noexcept
{
BS_ASSERT(min < max);

int num_bits = static_cast<int>(utility::bits_in_range(min, max));
uint32_t num_bits = utility::bits_in_range(min, max);

BS_ASSERT(num_bits <= sizeof(T) * 8);

if constexpr (sizeof(T) > 4)
{
// If the given range is bigger than a word (32 bits)
value = 0;
uint32_t unsigned_value;
if (num_bits > 32)
{
// If the given range is bigger than a word (32 bits)
value = 0;
uint32_t unsigned_value;

BS_ASSERT(reader.serialize_bits(unsigned_value, 32));
value |= static_cast<T>(unsigned_value);
BS_ASSERT(reader.serialize_bits(unsigned_value, 32));
value |= static_cast<T>(unsigned_value);

BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits - 32));
value |= static_cast<T>(unsigned_value) << 32;
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits - 32));
value |= static_cast<T>(unsigned_value) << 32;

value += min;
}
else
{
// If the given range is smaller than or equal to a word (32 bits)
uint32_t unsigned_value;
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits));
value += min;

value = static_cast<T>(unsigned_value) + min;
BS_ASSERT(value >= min && value <= max);

return true;
}
}

// If the given range is smaller than or equal to a word (32 bits)
uint32_t unsigned_value;
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits));

value = static_cast<T>(unsigned_value) + min;

BS_ASSERT(value >= min && value < max);
BS_ASSERT(value >= min && value <= max);

return true;
}
Expand All @@ -104,13 +112,13 @@ namespace bitstream
template<typename T, T Min, T Max>
struct serialize_traits<bounded_int<T, Min, Max>, typename std::enable_if_t<std::is_integral_v<T>>>
{
static bool serialize(bit_writer& writer, const T& value) noexcept
static bool serialize(bit_writer& writer, T value) noexcept
{
static_assert(Min < Max);

BS_ASSERT(value >= Min && value < Max);
BS_ASSERT(value >= Min && value <= Max);

constexpr int num_bits = static_cast<int>(utility::bits_in_range(Min, Max));
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);

static_assert(num_bits <= sizeof(T) * 8);

Expand All @@ -137,7 +145,7 @@ namespace bitstream
{
static_assert(Min < Max);

constexpr int num_bits = static_cast<int>(utility::bits_in_range(Min, Max));
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);

static_assert(num_bits <= sizeof(T) * 8);

Expand All @@ -164,7 +172,7 @@ namespace bitstream
value = static_cast<T>(unsigned_value) + Min;
}

BS_ASSERT(value >= Min && value < Max);
BS_ASSERT(value >= Min && value <= Max);

return true;
}
Expand Down
18 changes: 10 additions & 8 deletions test/src/test/serialize_array_traits.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <bitstream/stream/bit_writer.h>

#include <bitstream/traits/array_traits.h>
#include <bitstream/traits/bool_trait.h>
#include <bitstream/traits/integral_traits.h>

namespace bitstream::test::traits
Expand All @@ -13,33 +14,34 @@ namespace bitstream::test::traits
{
using trait = array_subset<uint32_t, bounded_int<uint32_t, 0U, 2048U>>;

// Test half precision float
uint32_t values_in[5]
// Test array subset
uint32_t values_in[6]
{
10,
21,
42,
99,
420,
1337
};

auto compare = [](uint32_t value) { return value != 21 && value != 42 && value != 99; };

uint8_t buffer[16]{ 0 };
bit_writer writer(buffer, 16);

auto compare = [](uint32_t value) { return value != 21 && value != 99; };

BS_TEST_ASSERT(writer.serialize<trait>(values_in, 5U, compare)); // Use bounded_int for writing
BS_TEST_ASSERT(writer.serialize<trait>(values_in, 6, compare)); // Use bounded_int for writing
uint32_t num_bytes = writer.flush();

BS_TEST_ASSERT_OPERATION(num_bytes, == , 6);


uint32_t values_out[5];
uint32_t values_out[6];
bit_reader reader(std::move(writer));

BS_TEST_ASSERT(reader.serialize<array_subset<uint32_t>>(values_out, 5U, compare, 0U, 2048U)); // Use min, max arguments for reading
BS_TEST_ASSERT(reader.serialize<array_subset<uint32_t>>(values_out, 6, compare, 0U, 2048U)); // Use min, max arguments for reading

for (int i = 0; i < 5; i++)
for (int i = 0; i < 6; i++)
{
if (!compare(values_in[i]))
continue;
Expand Down
Loading

0 comments on commit 7206d9f

Please sign in to comment.