Skip to content

Commit 7206d9f

Browse files
authored
Merge pull request #5 from KredeGC/dev
Add variable length encoding to array_traits
2 parents baf6091 + 3e428db commit 7206d9f

File tree

5 files changed

+242
-70
lines changed

5 files changed

+242
-70
lines changed

include/bitstream/traits/array_traits.h

Lines changed: 92 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#pragma once
22
#include "../utility/assert.h"
3-
#include "../utility/bits.h"
43

54
#include "../stream/serialize_traits.h"
65
#include "../stream/bit_reader.h"
76
#include "../stream/bit_writer.h"
87

8+
#include "../traits/bool_trait.h"
9+
#include "../traits/integral_traits.h"
10+
911
#include <cstdint>
1012

1113
namespace bitstream
@@ -16,49 +18,113 @@ namespace bitstream
1618
template<typename T, typename Trait>
1719
struct serialize_traits<array_subset<T, Trait>>
1820
{
21+
private:
22+
template<uint32_t Min, uint32_t Max, typename Stream>
23+
static bool serialize_difference(Stream& stream, int& previous, int& current, uint32_t& difference)
24+
{
25+
bool use_bits;
26+
if (Stream::writing)
27+
use_bits = difference <= Max;
28+
BS_ASSERT(stream.template serialize<bool>(use_bits));
29+
if (use_bits)
30+
{
31+
using bounded_trait = bounded_int<uint32_t, Min, Max>;
32+
33+
BS_ASSERT(stream.template serialize<bounded_trait>(difference));
34+
if (Stream::reading)
35+
current = previous + difference;
36+
previous = current;
37+
return true;
38+
}
39+
40+
return false;
41+
}
42+
43+
template<typename Stream>
44+
static bool serialize_index(Stream& stream, int& previous, int& current, int max_size)
45+
{
46+
uint32_t difference;
47+
if (Stream::writing)
48+
{
49+
BS_ASSERT(previous < current);
50+
difference = current - previous;
51+
BS_ASSERT(difference > 0);
52+
}
53+
54+
// +1 (1 bit)
55+
bool plus_one;
56+
if (Stream::writing)
57+
plus_one = difference == 1;
58+
BS_ASSERT(stream.template serialize<bool>(plus_one));
59+
if (plus_one)
60+
{
61+
if (Stream::reading)
62+
current = previous + 1;
63+
previous = current;
64+
return true;
65+
}
66+
67+
// [+2,5] -> [0,3] (2 bits)
68+
if (serialize_difference<2, 5>(stream, previous, current, difference))
69+
return true;
70+
71+
// [6,13] -> [0,7] (3 bits)
72+
if (serialize_difference<6, 13>(stream, previous, current, difference))
73+
return true;
74+
75+
// [14,29] -> [0,15] (4 bits)
76+
if (serialize_difference<14, 29>(stream, previous, current, difference))
77+
return true;
78+
79+
// [30,61] -> [0,31] (5 bits)
80+
if (serialize_difference<30, 61>(stream, previous, current, difference))
81+
return true;
82+
83+
// [62,125] -> [0,63] (6 bits)
84+
if (serialize_difference<62, 125>(stream, previous, current, difference))
85+
return true;
86+
87+
// [126,MaxObjects+1]
88+
BS_ASSERT(stream.template serialize<uint32_t>(difference, 126, max_size));
89+
if (Stream::reading)
90+
current = previous + difference;
91+
previous = current;
92+
return true;
93+
}
94+
95+
public:
1996
template<typename Compare, typename... Args>
20-
static bool serialize(bit_writer& writer, T* values, uint32_t max_size, Compare compare, Args&&... args) noexcept
97+
static bool serialize(bit_writer& writer, T* values, int max_size, Compare compare, Args&&... args) noexcept
2198
{
22-
uint32_t prev_index = 0;
23-
for (uint32_t i = 0; i < max_size; i++)
99+
int prev_index = -1;
100+
for (int index = 0; index < max_size; index++)
24101
{
25-
if (!compare(values[i]))
102+
if (!compare(values[index]))
26103
continue;
27104

28-
uint32_t num_bits = utility::bits_in_range(prev_index, max_size);
29-
30-
uint32_t index = i - prev_index;
31-
BS_ASSERT(writer.serialize_bits(index, num_bits));
32-
33-
BS_ASSERT(writer.serialize<Trait>(values[i], std::forward<Args>(args)...));
105+
BS_ASSERT(serialize_index(writer, prev_index, index, max_size));
34106

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

38-
uint32_t num_bits = utility::bits_in_range(prev_index, max_size);
39-
40-
BS_ASSERT(writer.serialize_bits(max_size - prev_index, num_bits));
110+
BS_ASSERT(serialize_index(writer, prev_index, max_size, max_size));
41111

42112
return true;
43113
}
44114

45115
template<typename Compare, typename... Args>
46-
static bool serialize(bit_reader& reader, T* values, uint32_t max_size, Compare compare, Args&&... args) noexcept
116+
static bool serialize(bit_reader& reader, T* values, int max_size, Compare compare, Args&&... args) noexcept
47117
{
48-
uint32_t prev_index = 0;
49-
for (uint32_t i = 0; i <= max_size; i++)
118+
int prev_index = -1;
119+
int index = 0;
120+
while (true)
50121
{
51-
uint32_t num_bits = utility::bits_in_range(prev_index, max_size);
52-
53-
uint32_t index;
54-
BS_ASSERT(reader.serialize_bits(index, num_bits));
122+
BS_ASSERT(serialize_index(reader, prev_index, index, max_size));
55123

56-
if (index + prev_index == max_size)
124+
if (index == max_size)
57125
break;
58126

59-
BS_ASSERT(reader.serialize<Trait>(values[index + prev_index], std::forward<Args>(args)...));
60-
61-
prev_index += index;
127+
BS_ASSERT(reader.serialize<Trait>(values[index], std::forward<Args>(args)...));
62128
}
63129

64130
return true;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
#pragma once
2+
#include "../utility/assert.h"
3+
4+
#include "../stream/serialize_traits.h"
5+
#include "../stream/bit_reader.h"
6+
#include "../stream/bit_writer.h"
7+
8+
namespace bitstream
9+
{
10+
/**
11+
* @brief A trait used to serialize a boolean as a single bit
12+
*/
13+
template<>
14+
struct serialize_traits<bool>
15+
{
16+
static bool serialize(bit_writer& writer, bool value) noexcept
17+
{
18+
uint32_t unsigned_value = value;
19+
20+
return writer.serialize_bits(unsigned_value, 1U);
21+
}
22+
23+
static bool serialize(bit_reader& reader, bool& value) noexcept
24+
{
25+
uint32_t unsigned_value;
26+
27+
BS_ASSERT(reader.serialize_bits(unsigned_value, 1U));
28+
29+
value = unsigned_value;
30+
31+
return true;
32+
}
33+
};
34+
}

include/bitstream/traits/integral_traits.h

Lines changed: 44 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -27,67 +27,75 @@ namespace bitstream
2727
template<typename T>
2828
struct serialize_traits<T, typename std::enable_if_t<std::is_integral_v<T>>>
2929
{
30-
static bool serialize(bit_writer& writer, const T& value, T min = (std::numeric_limits<T>::min)(), T max = (std::numeric_limits<T>::max)()) noexcept
30+
static bool serialize(bit_writer& writer, T value, T min = (std::numeric_limits<T>::min)(), T max = (std::numeric_limits<T>::max)()) noexcept
3131
{
3232
BS_ASSERT(min < max);
3333

34-
BS_ASSERT(value >= min && value < max);
34+
BS_ASSERT(value >= min && value <= max);
3535

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

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

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

46-
unsigned_value = static_cast<uint32_t>((value - min) >> 32);
47-
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits - 32));
48-
}
49-
else
50-
{
51-
// If the given range is smaller than or equal to a word (32 bits)
52-
uint32_t unsigned_value = static_cast<uint32_t>(value - min);
53-
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits));
48+
unsigned_value = static_cast<uint32_t>((value - min) >> 32);
49+
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits - 32));
50+
51+
return true;
52+
}
5453
}
5554

55+
// If the given range is smaller than or equal to a word (32 bits)
56+
uint32_t unsigned_value = static_cast<uint32_t>(value - min);
57+
BS_ASSERT(writer.serialize_bits(unsigned_value, num_bits));
58+
5659
return true;
5760
}
5861

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

63-
int num_bits = static_cast<int>(utility::bits_in_range(min, max));
66+
uint32_t num_bits = utility::bits_in_range(min, max);
6467

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

6770
if constexpr (sizeof(T) > 4)
6871
{
69-
// If the given range is bigger than a word (32 bits)
70-
value = 0;
71-
uint32_t unsigned_value;
72+
if (num_bits > 32)
73+
{
74+
// If the given range is bigger than a word (32 bits)
75+
value = 0;
76+
uint32_t unsigned_value;
7277

73-
BS_ASSERT(reader.serialize_bits(unsigned_value, 32));
74-
value |= static_cast<T>(unsigned_value);
78+
BS_ASSERT(reader.serialize_bits(unsigned_value, 32));
79+
value |= static_cast<T>(unsigned_value);
7580

76-
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits - 32));
77-
value |= static_cast<T>(unsigned_value) << 32;
81+
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits - 32));
82+
value |= static_cast<T>(unsigned_value) << 32;
7883

79-
value += min;
80-
}
81-
else
82-
{
83-
// If the given range is smaller than or equal to a word (32 bits)
84-
uint32_t unsigned_value;
85-
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits));
84+
value += min;
8685

87-
value = static_cast<T>(unsigned_value) + min;
86+
BS_ASSERT(value >= min && value <= max);
87+
88+
return true;
89+
}
8890
}
91+
92+
// If the given range is smaller than or equal to a word (32 bits)
93+
uint32_t unsigned_value;
94+
BS_ASSERT(reader.serialize_bits(unsigned_value, num_bits));
95+
96+
value = static_cast<T>(unsigned_value) + min;
8997

90-
BS_ASSERT(value >= min && value < max);
98+
BS_ASSERT(value >= min && value <= max);
9199

92100
return true;
93101
}
@@ -104,13 +112,13 @@ namespace bitstream
104112
template<typename T, T Min, T Max>
105113
struct serialize_traits<bounded_int<T, Min, Max>, typename std::enable_if_t<std::is_integral_v<T>>>
106114
{
107-
static bool serialize(bit_writer& writer, const T& value) noexcept
115+
static bool serialize(bit_writer& writer, T value) noexcept
108116
{
109117
static_assert(Min < Max);
110118

111-
BS_ASSERT(value >= Min && value < Max);
119+
BS_ASSERT(value >= Min && value <= Max);
112120

113-
constexpr int num_bits = static_cast<int>(utility::bits_in_range(Min, Max));
121+
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);
114122

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

@@ -137,7 +145,7 @@ namespace bitstream
137145
{
138146
static_assert(Min < Max);
139147

140-
constexpr int num_bits = static_cast<int>(utility::bits_in_range(Min, Max));
148+
constexpr uint32_t num_bits = utility::bits_in_range(Min, Max);
141149

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

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

167-
BS_ASSERT(value >= Min && value < Max);
175+
BS_ASSERT(value >= Min && value <= Max);
168176

169177
return true;
170178
}

test/src/test/serialize_array_traits.cpp

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <bitstream/stream/bit_writer.h>
66

77
#include <bitstream/traits/array_traits.h>
8+
#include <bitstream/traits/bool_trait.h>
89
#include <bitstream/traits/integral_traits.h>
910

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

16-
// Test half precision float
17-
uint32_t values_in[5]
17+
// Test array subset
18+
uint32_t values_in[6]
1819
{
1920
10,
2021
21,
2122
42,
2223
99,
24+
420,
2325
1337
2426
};
2527

28+
auto compare = [](uint32_t value) { return value != 21 && value != 42 && value != 99; };
29+
2630
uint8_t buffer[16]{ 0 };
2731
bit_writer writer(buffer, 16);
2832

29-
auto compare = [](uint32_t value) { return value != 21 && value != 99; };
30-
31-
BS_TEST_ASSERT(writer.serialize<trait>(values_in, 5U, compare)); // Use bounded_int for writing
33+
BS_TEST_ASSERT(writer.serialize<trait>(values_in, 6, compare)); // Use bounded_int for writing
3234
uint32_t num_bytes = writer.flush();
3335

3436
BS_TEST_ASSERT_OPERATION(num_bytes, == , 6);
3537

3638

37-
uint32_t values_out[5];
39+
uint32_t values_out[6];
3840
bit_reader reader(std::move(writer));
3941

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

42-
for (int i = 0; i < 5; i++)
44+
for (int i = 0; i < 6; i++)
4345
{
4446
if (!compare(values_in[i]))
4547
continue;

0 commit comments

Comments
 (0)