Skip to content

Commit 6eeae1a

Browse files
authored
Merge pull request #9 from KredeGC/dev
Constexpr and CRC correctness
2 parents ed26f51 + 6f389f7 commit 6eeae1a

17 files changed

+142
-108
lines changed

README.md

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ bool status_read = reader.serialize<bool>(out_value);
9898
## Bounded integers - T
9999
A trait that covers all signed and unsigned integers.<br/>
100100
Takes the integer by reference and a lower and upper bound.<br/>
101-
The upper and lower bounds will default to T's upper and lower bound if left unspecified, effectively making the object unbounded.
101+
The upper and lower bounds will default to T's upper and lower bounds if left unspecified, effectively making the object unbounded.
102102

103103
The call signature can be seen below:
104104
```cpp
@@ -115,7 +115,8 @@ bool status_read = reader.serialize<int16_t>(out_value, -512, 2098);
115115
## Compile-time bounded integers - bounded_int\<T, T Min, T Max\>
116116
A trait that covers all signed and unsigned integers within a `bounded_int` wrapper.<br/>
117117
Takes the integer by reference and a lower and upper bound as template parameters.<br/>
118-
This is preferable if you know the bounds at compile time as it skips having to calculate the number of bits required.
118+
This is preferable if you know the bounds at compile time as it skips having to calculate the number of bits required.<br/>
119+
The upper and lower bounds will default to T's upper and lower bounds if left unspecified, effectively making the object unbounded.
119120

120121
The call signature can be seen below:
121122
```cpp
@@ -163,6 +164,22 @@ bool status_write = writer.serialize<std::string>(in_value, 32);
163164
bool status_read = reader.serialize<std::string>(out_value, 32);
164165
```
165166

167+
## Double-precision float - double
168+
A trait that covers an entire double, with no quantization.<br/>
169+
Takes a reference to the double.
170+
171+
The call signature can be seen below:
172+
```cpp
173+
bool serialize<double>(double& value);
174+
```
175+
As well as a short example of its usage:
176+
```cpp
177+
double in_value = 0.12345678652;
178+
double out_value;
179+
bool status_write = writer.serialize<double>(in_value);
180+
bool status_read = reader.serialize<double>(out_value);
181+
```
182+
166183
## Single-precision float - float
167184
A trait that covers an entire float, with no quantization.<br/>
168185
Takes a reference to the float.
@@ -256,7 +273,7 @@ writer.serialize_bits(value, 5);
256273
// Flush the writer's remaining state into the buffer
257274
uint32_t num_bits = writer.flush();
258275
259-
// Create a reader, referencing the buffer and bytes written
276+
// Create a reader, referencing the buffer and bits written
260277
bit_reader reader(buffer, num_bits);
261278
262279
// Read the value back
@@ -277,7 +294,7 @@ writer.serialize<int32_t>(value, -90, 40); // A lower and upper bound which the
277294
// Flush the writer's remaining state into the buffer
278295
uint32_t num_bits = writer.flush();
279296

280-
// Create a reader by moving and invalidating the writer
297+
// Create a reader, referencing the buffer and bits written
281298
bit_reader reader(buffer, num_bits);
282299

283300
// Read the value back
@@ -298,7 +315,7 @@ writer.serialize<const char*>(value, 32U); // The second argument is the maximum
298315
// Flush the writer's remaining state into the buffer
299316
uint32_t num_bits = writer.flush();
300317
301-
// Create a reader by moving and invalidating the writer
318+
// Create a reader, referencing the buffer and bits written
302319
bit_reader reader(buffer, num_bits);
303320
304321
// Read the value back
@@ -319,7 +336,7 @@ writer.serialize<std::string>(value, 32U); // The second argument is the maximum
319336
// Flush the writer's remaining state into the buffer
320337
uint32_t num_bits = writer.flush();
321338

322-
// Create a reader by moving and invalidating the writer
339+
// Create a reader, referencing the buffer and bits written
323340
bit_reader reader(buffer, num_bits);
324341

325342
// Read the value back
@@ -341,7 +358,7 @@ writer.serialize<bounded_range>(range, value);
341358
// Flush the writer's remaining state into the buffer
342359
uint32_t num_bits = writer.flush();
343360
344-
// Create a reader by moving and invalidating the writer
361+
// Create a reader, referencing the buffer and bits written
345362
bit_reader reader(buffer, num_bits);
346363
347364
// Read the value back
@@ -374,6 +391,9 @@ struct serialize_traits<TRAIT_TYPE> // The type to use when referencing this spe
374391
};
375392
```
376393
394+
As with any functions, you are free to overload them if you want to serialize an object differently, depending on any parameters you pass.
395+
As long as their list of parameters starts with `bit_writer&` and `bit_reader&` respectively they will be able to be called.
396+
377397
## Unified serialization
378398
The serialization can also be unified with templating, if writing and reading look similar.
379399
If some parts of the serialization process don't match entirely you can query the `Stream::reading` or `Stream::writing` and branch depending on the value.

include/bitstream/quantization/bounded_range.h

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,26 @@ namespace bitstream
3232
class bounded_range
3333
{
3434
public:
35-
bounded_range() = default;
35+
constexpr bounded_range() noexcept :
36+
m_Min(0),
37+
m_Max(0),
38+
m_Precision(0),
39+
m_BitsRequired(0),
40+
m_Mask(0) {}
3641

37-
bounded_range(float min, float max, float precision) :
42+
constexpr bounded_range(float min, float max, float precision) noexcept :
3843
m_Min(min),
3944
m_Max(max),
40-
m_Precision(precision)
41-
{
42-
m_BitsRequired = log2(static_cast<uint32_t>((m_Max - m_Min) * (1.0f / precision) + 0.5f)) + 1;
43-
m_Mask = (1 << m_BitsRequired) - 1;
44-
}
45+
m_Precision(precision),
46+
m_BitsRequired(log2(static_cast<uint32_t>((m_Max - m_Min) * (1.0f / precision) + 0.5f)) + 1),
47+
m_Mask((1U << m_BitsRequired) - 1U) {}
4548

46-
inline float get_min() const { return m_Min; }
47-
inline float get_max() const { return m_Max; }
48-
inline float get_precision() const { return m_Precision; }
49-
inline uint32_t get_bits_required() const { return m_BitsRequired; }
49+
constexpr inline float get_min() const noexcept { return m_Min; }
50+
constexpr inline float get_max() const noexcept { return m_Max; }
51+
constexpr inline float get_precision() const noexcept { return m_Precision; }
52+
constexpr inline uint32_t get_bits_required() const noexcept { return m_BitsRequired; }
5053

51-
inline uint32_t quantize(float value) const
54+
constexpr inline uint32_t quantize(float value) const noexcept
5255
{
5356
if (value < m_Min)
5457
value = m_Min;
@@ -58,7 +61,7 @@ namespace bitstream
5861
return static_cast<uint32_t>(static_cast<float>((value - m_Min) * (1.0f / m_Precision)) + 0.5f) & m_Mask;
5962
}
6063

61-
inline float dequantize(uint32_t data) const
64+
constexpr inline float dequantize(uint32_t data) const noexcept
6265
{
6366
float adjusted = (static_cast<float>(data) * m_Precision) + m_Min;
6467

@@ -71,7 +74,7 @@ namespace bitstream
7174
}
7275

7376
private:
74-
inline static constexpr uint32_t log2(uint32_t value)
77+
constexpr inline static uint32_t log2(uint32_t value) noexcept
7578
{
7679
value |= value >> 1;
7780
value |= value >> 2;
@@ -90,7 +93,7 @@ namespace bitstream
9093
uint32_t m_BitsRequired;
9194
uint32_t m_Mask;
9295

93-
inline static constexpr uint32_t DE_BRUIJN[32]
96+
constexpr inline static uint32_t DE_BRUIJN[32]
9497
{
9598
0, 9, 1, 10, 13, 21, 2, 29,
9699
11, 14, 16, 18, 22, 25, 3, 30,

include/bitstream/quantization/half_precision.h

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ namespace bitstream
3333
class half_precision
3434
{
3535
public:
36-
inline static uint16_t quantize(float value)
36+
inline static uint16_t quantize(float value) noexcept
3737
{
3838
int32_t tmp;
3939
std::memcpy(&tmp, &value, sizeof(float));
@@ -44,7 +44,7 @@ namespace bitstream
4444

4545
if (e <= 0) {
4646
if (e < -10)
47-
return (uint16_t)s;
47+
return static_cast<uint16_t>(s);
4848

4949
m |= 0x00800000;
5050

@@ -54,16 +54,16 @@ namespace bitstream
5454

5555
m = (m + a + b) >> t;
5656

57-
return (uint16_t)(s | m);
57+
return static_cast<uint16_t>(s | m);
5858
}
5959

6060
if (e == 0XFF - (127 - 15)) {
6161
if (m == 0)
62-
return (uint16_t)(s | 0X7C00);
62+
return static_cast<uint16_t>(s | 0X7C00);
6363

6464
m >>= 13;
6565

66-
return (uint16_t)(s | 0X7C00 | m | ((m == 0) ? 1 : 0));
66+
return static_cast<uint16_t>(s | 0X7C00 | m | ((m == 0) ? 1 : 0));
6767
}
6868

6969
m = m + 0X00000FFF + ((m >> 13) & 1);
@@ -74,15 +74,15 @@ namespace bitstream
7474
}
7575

7676
if (e > 30)
77-
return (uint16_t)(s | 0X7C00);
77+
return static_cast<uint16_t>(s | 0X7C00);
7878

79-
return (uint16_t)(s | (e << 10) | (m >> 13));
79+
return static_cast<uint16_t>(s | (e << 10) | (m >> 13));
8080
}
8181

82-
inline static float dequantize(uint16_t value)
82+
inline static float dequantize(uint16_t value) noexcept
8383
{
8484
uint32_t tmp;
85-
uint32_t mantissa = (uint32_t)(value & 1023);
85+
uint32_t mantissa = static_cast<uint32_t>(value & 1023);
8686
uint32_t exponent = 0XFFFFFFF2;
8787

8888
if ((value & -33792) == 0) {
@@ -93,16 +93,16 @@ namespace bitstream
9393
}
9494

9595
mantissa &= 0XFFFFFBFF;
96-
tmp = (((uint32_t)value & 0x8000) << 16) | ((exponent + 127) << 23) | (mantissa << 13);
96+
tmp = ((static_cast<uint32_t>(value) & 0x8000) << 16) | ((exponent + 127) << 23) | (mantissa << 13);
9797
}
9898
else
9999
{
100-
tmp = (uint32_t)((value & 0x8000) << 16);
100+
tmp = static_cast<uint32_t>((value & 0x8000) << 16);
101101
}
102102
}
103103
else
104104
{
105-
tmp = ((((uint32_t)value & 0x8000) << 16) | ((((((uint32_t)value >> 10) & 0X1F) - 15) + 127) << 23)) | (mantissa << 13);
105+
tmp = ((static_cast<uint32_t>(value) & 0x8000) << 16) | (((((static_cast<uint32_t>(value) >> 10) & 0X1F) - 15) + 127) << 23) | (mantissa << 13);
106106
}
107107

108108
float result;

include/bitstream/quantization/smallest_three.h

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,14 @@ namespace bitstream
3737
uint32_t b;
3838
uint32_t c;
3939

40-
quantized_quaternion() = default;
41-
42-
quantized_quaternion(uint32_t m, uint32_t a, uint32_t b, uint32_t c)
43-
: m(m), a(a), b(b), c(c) {}
40+
constexpr quantized_quaternion() noexcept :
41+
m(0),
42+
a(0),
43+
b(0),
44+
c(0) {}
45+
46+
constexpr quantized_quaternion(uint32_t w, uint32_t x, uint32_t y, uint32_t z) noexcept :
47+
m(w), a(x), b(y), c(z) {}
4448
};
4549

4650
/**
@@ -55,7 +59,7 @@ namespace bitstream
5559
static constexpr float SMALLEST_THREE_PACK = 1.0f / SMALLEST_THREE_UNPACK;
5660

5761
public:
58-
inline static quantized_quaternion quantize(const T& quaternion)
62+
inline static quantized_quaternion quantize(const T& quaternion) noexcept
5963
{
6064
constexpr float half_range = static_cast<float>(1 << (BitsPerElement - 1));
6165
constexpr float packer = SMALLEST_THREE_PACK * half_range;
@@ -71,7 +75,7 @@ namespace bitstream
7175
{
7276
float element = quaternion[i];
7377

74-
float abs = std::abs(element);
78+
float abs = element > 0.0f ? element : -element;
7579

7680
if (abs > max_value)
7781
{
@@ -111,28 +115,28 @@ namespace bitstream
111115

112116
if (sign_minus)
113117
{
114-
a = (uint32_t)((-af * packer) + half_range);
115-
b = (uint32_t)((-bf * packer) + half_range);
116-
c = (uint32_t)((-cf * packer) + half_range);
118+
a = static_cast<uint32_t>((-af * packer) + half_range);
119+
b = static_cast<uint32_t>((-bf * packer) + half_range);
120+
c = static_cast<uint32_t>((-cf * packer) + half_range);
117121
}
118122
else
119123
{
120-
a = (uint32_t)((af * packer) + half_range);
121-
b = (uint32_t)((bf * packer) + half_range);
122-
c = (uint32_t)((cf * packer) + half_range);
124+
a = static_cast<uint32_t>((af * packer) + half_range);
125+
b = static_cast<uint32_t>((bf * packer) + half_range);
126+
c = static_cast<uint32_t>((cf * packer) + half_range);
123127
}
124128

125129
return { m, a, b, c };
126130
}
127131

128-
inline static T dequantize(const quantized_quaternion& data)
132+
inline static T dequantize(const quantized_quaternion& data) noexcept
129133
{
130134
constexpr uint32_t half_range = (1 << (BitsPerElement - 1));
131135
constexpr float unpacker = SMALLEST_THREE_UNPACK * (1.0f / half_range);
132136

133-
float a = data.a * unpacker - half_range * unpacker;
134-
float b = data.b * unpacker - half_range * unpacker;
135-
float c = data.c * unpacker - half_range * unpacker;
137+
float a = static_cast<float>(data.a * unpacker - half_range * unpacker);
138+
float b = static_cast<float>(data.b * unpacker - half_range * unpacker);
139+
float c = static_cast<float>(data.c * unpacker - half_range * unpacker);
136140

137141
float d = std::sqrt(1.0f - ((a * a) + (b * b) + (c * c)));
138142

0 commit comments

Comments
 (0)