Skip to content

Commit d164daf

Browse files
committed
Add variable length encoding to array_traits
Add serialize_traits specialization for bool Add unit tests for bool trait Fix some issues with integral_traits
1 parent baf6091 commit d164daf

File tree

5 files changed

+241
-69
lines changed

5 files changed

+241
-69
lines changed

include/bitstream/traits/array_traits.h

Lines changed: 92 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#include "../stream/bit_reader.h"
77
#include "../stream/bit_writer.h"
88

9+
#include "../traits/bool_trait.h"
10+
#include "../traits/integral_traits.h"
11+
912
#include <cstdint>
1013

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

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)...));
106+
BS_ASSERT(serialize_index(writer, prev_index, index, max_size));
34107

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

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));
111+
BS_ASSERT(serialize_index(writer, prev_index, max_size, max_size));
41112

42113
return true;
43114
}
44115

45116
template<typename Compare, typename... Args>
46-
static bool serialize(bit_reader& reader, T* values, uint32_t max_size, Compare compare, Args&&... args) noexcept
117+
static bool serialize(bit_reader& reader, T* values, int max_size, Compare compare, Args&&... args) noexcept
47118
{
48-
uint32_t prev_index = 0;
49-
for (uint32_t i = 0; i <= max_size; i++)
119+
int prev_index = -1;
120+
int index = 0;
121+
while (true)
50122
{
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));
123+
BS_ASSERT(serialize_index(reader, prev_index, index, max_size));
55124

56-
if (index + prev_index == max_size)
125+
if (index == max_size)
57126
break;
58127

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

64131
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: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,34 @@ namespace bitstream::test::traits
1313
{
1414
using trait = array_subset<uint32_t, bounded_int<uint32_t, 0U, 2048U>>;
1515

16-
// Test half precision float
17-
uint32_t values_in[5]
16+
// Test array subset
17+
uint32_t values_in[6]
1818
{
1919
10,
2020
21,
2121
42,
2222
99,
23+
420,
2324
1337
2425
};
2526

27+
auto compare = [](uint32_t value) { return value != 21 && value != 42 && value != 99; };
28+
2629
uint8_t buffer[16]{ 0 };
2730
bit_writer writer(buffer, 16);
2831

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
32+
BS_TEST_ASSERT(writer.serialize<trait>(values_in, 6, compare)); // Use bounded_int for writing
3233
uint32_t num_bytes = writer.flush();
3334

3435
BS_TEST_ASSERT_OPERATION(num_bytes, == , 6);
3536

3637

37-
uint32_t values_out[5];
38+
uint32_t values_out[6];
3839
bit_reader reader(std::move(writer));
3940

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

42-
for (int i = 0; i < 5; i++)
43+
for (int i = 0; i < 6; i++)
4344
{
4445
if (!compare(values_in[i]))
4546
continue;

0 commit comments

Comments
 (0)