Skip to content

Commit f7f7e8b

Browse files
authored
Merge pull request #11 from KredeGC/dev
Add more assertions
2 parents 5780a19 + c66926d commit f7f7e8b

File tree

5 files changed

+249
-120
lines changed

5 files changed

+249
-120
lines changed

.github/workflows/dev.yml

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
name: Test
2+
3+
on:
4+
pull_request:
5+
branches: [ master ]
6+
paths-ignore:
7+
- '**/*.md'
8+
- '**/*.gitignore'
9+
- '**/Doxyfile'
10+
11+
jobs:
12+
Build:
13+
strategy:
14+
matrix:
15+
machine:
16+
- os: ubuntu-latest
17+
action: gmake2
18+
toolset: gcc
19+
- os: ubuntu-latest
20+
action: gmake2
21+
toolset: clang
22+
- os: windows-latest
23+
action: vs2019
24+
toolset: msc
25+
runs-on: ${{ matrix.machine.os }}
26+
permissions:
27+
actions: read
28+
contents: read
29+
security-events: write
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v3
33+
- name: Setup premake
34+
uses: abel0b/[email protected]
35+
with:
36+
version: "5.0.0-beta1"
37+
- name: Install GCC
38+
if: matrix.machine.os == 'ubuntu-latest' && matrix.machine.toolset == 'gcc'
39+
run: sudo apt-get update && sudo apt-get install -y gcc g++
40+
- name: Install Clang & LLVM
41+
if: matrix.machine.os == 'ubuntu-latest' && matrix.machine.toolset == 'clang'
42+
run: sudo apt-get update && sudo apt-get install -y clang llvm lld
43+
- name: Install msbuild to PATH
44+
if: matrix.machine.os == 'windows-latest' && matrix.machine.toolset == 'msc'
45+
uses: microsoft/[email protected]
46+
- name: Run premake
47+
run: premake5 ${{ matrix.machine.action }} --toolset=${{ matrix.machine.toolset }} --dialect=C++17
48+
- name: Build
49+
run: premake5 build --config=debug --architecture=x64
50+
- name: Run test
51+
run: premake5 test --config=debug --architecture=x64

.github/workflows/main.yml

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,6 @@ name: Test
33
on:
44
push:
55
branches: [ master ]
6-
pull_request:
7-
branches: [ master ]
8-
paths-ignore:
9-
- '**/*.md'
10-
- '**/*.gitignore'
11-
- '**/Doxyfile'
126

137
jobs:
148
Build:

README.md

Lines changed: 154 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -21,18 +21,20 @@ Based on [Glenn Fiedler's articles](https://gafferongames.com/post/reading_and_w
2121
* [Installation](#installation)
2222
* [Usage](#usage)
2323
* [Documentation](#documentation)
24+
* [Serialization Examples](#serialization-examples)
2425
* [Serializables - serialize_traits](#serializables---serialize_traits)
2526
* [Booleans - bool](#booleans---bool)
2627
* [Bounded integers - T](#bounded-integers---t)
2728
* [Compile-time bounded integers - bounded_int\<T, T Min, T Max\>](#compile-time-bounded-integers---bounded_intt-t-min-t-max)
2829
* [C-style strings - const char*](#c-style-strings---const-char)
30+
* [Compile-time bounded C-style strings - bounded_string\<const char*, Max\>](#compile-time-bounded-c-style-strings---bounded_stringconst-char-max)
2931
* [Modern strings - std::basic_string\<T\>](#modern-strings---stdbasic_stringt)
32+
* [Compile-time bounded Modern strings - bounded_string\<std::basic_string\<T\>, Max\>](#compile-time-bounded-modern-strings---bounded_stringstdbasic_stringt-max)
3033
* [Double-precision float - double](#double-precision-float---double)
3134
* [Single-precision float - float](#single-precision-float---float)
3235
* [Half-precision float - half_precision](#half-precision-float---half_precision)
3336
* [Bounded float - bounded_range](#bounded-float---bounded_range)
3437
* [Quaternion - smallest_three\<Q, BitsPerElement\>](#quaternion---smallest_threeq-bitsperelement)
35-
* [Serialization Examples](#serialization-examples)
3638
* [Extensibility](#extensibility)
3739
* [Adding new serializables types](#adding-new-serializables-types)
3840
* [Unified serialization](#unified-serialization)
@@ -54,7 +56,7 @@ The header files can either be downloaded from the [releases page](https://githu
5456
The source and header files inside the `src/` directory are only tests and should not be included into your project, unless you wish to test the library as part of your pipeline.
5557

5658
# Usage
57-
The library has a global header file ([`bitstream/bitstream.h`](https://github.com/KredeGC/BitStream/tree/master/include/bitstream/bitstream.h)) which includes every other header file in the library.
59+
The library has a main header file ([`bitstream/bitstream.h`](https://github.com/KredeGC/BitStream/tree/master/include/bitstream/bitstream.h)) which includes every other header file in the library.
5860

5961
If you only need certain features you can instead opt to just include the files you need.
6062
The files are stored in categories:
@@ -76,6 +78,117 @@ You can also look at the unit tests to get a better idea about what you can expe
7678
# Documentation
7779
Refer to [the documentation](https://kredegc.github.io/BitStream/namespaces.html) for more information about what different classes provide.
7880

81+
# Serialization Examples
82+
The examples below follow the same structure: First writing to a buffer and then reading from it. Each example is littered with comments about the procedure, as well as what outcome is expected.
83+
84+
Writing the first 5 bits of an int to the buffer, then reading it back:
85+
```cpp
86+
// Create a writer, referencing the buffer and its size
87+
alignas(uint32_t) uint8_t buffer[4]; // Buffer must be a multiple of 4 bytes / 32 bits and 4-byte-aligned
88+
bit_writer writer(buffer, 4);
89+
90+
// Write the value
91+
uint32_t value = 27; // We can choose any value below 2^5. Otherwise we need more than 5 bits
92+
writer.serialize_bits(value, 5);
93+
94+
// Flush the writer's remaining state into the buffer
95+
uint32_t num_bits = writer.flush();
96+
97+
// Create a reader, referencing the buffer and bits written
98+
bit_reader reader(buffer, num_bits);
99+
100+
// Read the value back
101+
uint32_t out_value; // We don't have to initialize it yet
102+
reader.serialize_bits(out_value, 5); // out_value should now have a value of 27
103+
```
104+
105+
Writing a signed int to the buffer, within a range:
106+
```cpp
107+
// Create a writer, referencing the buffer and its size
108+
byte_buffer<4> buffer; // byte_bufer is just a wrapper for a 4-byte aligned buffer
109+
bit_writer writer(buffer);
110+
111+
// Write the value
112+
int32_t value = -45; // We can choose any value within the range below
113+
writer.serialize<int32_t>(value, -90, 40); // A lower and upper bound which the value will be quantized between
114+
115+
// Flush the writer's remaining state into the buffer
116+
uint32_t num_bits = writer.flush();
117+
118+
// Create a reader, referencing the buffer and bits written
119+
bit_reader reader(buffer, num_bits);
120+
121+
// Read the value back
122+
int32_t out_value; // We don't have to initialize it yet
123+
reader.serialize<int32_t>(out_value, -90, 40); // out_value should now have a value of -45
124+
```
125+
126+
Writing a c-style string into the buffer:
127+
```cpp
128+
// Create a writer, referencing the buffer and its size
129+
byte_buffer<32> buffer;
130+
bit_writer writer(buffer);
131+
132+
// Write the value
133+
const char* value = "Hello world!";
134+
writer.serialize<const char*>(value, 32U); // The second argument is the maximum size we expect the string to be
135+
136+
// Flush the writer's remaining state into the buffer
137+
uint32_t num_bits = writer.flush();
138+
139+
// Create a reader, referencing the buffer and bits written
140+
bit_reader reader(buffer, num_bits);
141+
142+
// Read the value back
143+
char out_value[32]; // Set the size to the max size
144+
reader.serialize<const char*>(out_value, 32U); // out_value should now contain "Hello world!\0"
145+
```
146+
147+
Writing a std::string into the buffer:
148+
```cpp
149+
// Create a writer, referencing the buffer and its size
150+
byte_buffer<32> buffer;
151+
bit_writer writer(buffer);
152+
153+
// Write the value
154+
std::string value = "Hello world!";
155+
writer.serialize<std::string>(value, 32U); // The second argument is the maximum size we expect the string to be
156+
157+
// Flush the writer's remaining state into the buffer
158+
uint32_t num_bits = writer.flush();
159+
160+
// Create a reader, referencing the buffer and bits written
161+
bit_reader reader(buffer, num_bits);
162+
163+
// Read the value back
164+
std::string out_value; // The string will be resized if the output doesn't fit
165+
reader.serialize<std::string>(out_value, 32U); // out_value should now contain "Hello world!"
166+
```
167+
168+
Writing a float into the buffer with a bounded range and precision:
169+
```cpp
170+
// Create a writer, referencing the buffer and its size
171+
byte_buffer<4> buffer;
172+
bit_writer writer(buffer);
173+
174+
// Write the value
175+
bounded_range range(1.0f, 4.0f, 1.0f / 128.0f); // Min, Max, Precision
176+
float value = 1.2345678f;
177+
writer.serialize<bounded_range>(range, value);
178+
179+
// Flush the writer's remaining state into the buffer
180+
uint32_t num_bits = writer.flush();
181+
182+
// Create a reader, referencing the buffer and bits written
183+
bit_reader reader(buffer, num_bits);
184+
185+
// Read the value back
186+
float out_value;
187+
reader.serialize<bounded_range>(range, out_value); // out_value should now be a value close to 1.2345678f
188+
```
189+
190+
These examples can also be seen in [`src/test/examples_test.cpp`](https://github.com/KredeGC/BitStream/tree/master/src/test/examples_test.cpp).
191+
79192
# Serializables - serialize_traits
80193
Below is a noncomprehensive list of serializable traits.
81194
A big feature of the library is extensibility, which is why you can add your own types as you please, or choose not to include specific types if you don't need them.
@@ -133,7 +246,9 @@ bool status_read = reader.serialize<bounded_int<int16_t, -512, 2098>>(out_value)
133246

134247
## C-style strings - const char*
135248
A trait that only covers c-style strings.<br/>
136-
Takes the pointer and a maximum expected string length.
249+
Takes the pointer and a maximum expected string length.<br/>
250+
Note: In C++20 UTF-8 strings were given their own type, which means that you either have to cast your `char8_t*` to a `char*`
251+
or use `serialize<const char8_t*>` instead.
137252

138253
The call signature can be seen below:
139254
```cpp
@@ -147,6 +262,23 @@ bool status_write = writer.serialize<const char*>(in_value, 32);
147262
bool status_read = reader.serialize<const char*>(out_value, 32);
148263
```
149264
265+
## Compile-time bounded C-style strings - bounded_string\<const char*, Max\>
266+
A trait that only covers c-style strings.<br/>
267+
Takes the pointer as argument and a maximum expected string length as template parameter.<br/>
268+
This is preferable if you know the maximum length at compile time as it skips having to calculate the number of bits required.
269+
270+
The call signature can be seen below:
271+
```cpp
272+
bool serialize<bounded_string<const char*, MaxSize>>(const char* value);
273+
```
274+
As well as a short example of its usage:
275+
```cpp
276+
const char* in_value = "Hello world!";
277+
char out_value[32]{ 0 };
278+
bool status_write = writer.serialize<bounded_string<const char*, 32U>>(in_value);
279+
bool status_read = reader.serialize<bounded_string<const char*, 32U>>(out_value);
280+
```
281+
150282
## Modern strings - std::basic_string\<T\>
151283
A trait that covers any combination of basic_string, including strings with different allocators.<br/>
152284
Takes a reference to the string and a maximum expected string length.
@@ -165,6 +297,25 @@ bool status_write = writer.serialize<std::string>(in_value, 32);
165297
bool status_read = reader.serialize<std::string>(out_value, 32);
166298
```
167299

300+
## Compile-time bounded Modern strings - bounded_string\<std::basic_string\<T\>, Max\>
301+
A trait that covers any combination of basic_string, including strings with different allocators.<br/>
302+
Takes a reference to the string as argument and a maximum expected string length as template parameter.<br/>
303+
This is preferable if you know the maximum length at compile time as it skips having to calculate the number of bits required.
304+
305+
The, somewhat bloated, call signature can be seen below:
306+
```cpp
307+
bool serialize<bounded_string<std::basic_string<T, Traits, Alloc>, MaxSize>>(std::basic_string<T, Traits, Alloc>& value);
308+
// For std::string this would look like:
309+
bool serialize<bounded_string<std::string, MaxSize>>(std::string& value);
310+
```
311+
As well as a short example of its usage:
312+
```cpp
313+
std::string in_value = "Hello world!";
314+
std::string out_value;
315+
bool status_write = writer.serialize<bounded_string<std::string, 32U>>(in_value);
316+
bool status_read = reader.serialize<bounded_string<std::string, 32U>>(out_value);
317+
```
318+
168319
## Double-precision float - double
169320
A trait that covers an entire double, with no quantization.<br/>
170321
Takes a reference to the double.
@@ -258,117 +409,6 @@ bool status_write = writer.serialize<smallest_three<quaternion, 12>>(in_value);
258409
bool status_read = reader.serialize<smallest_three<quaternion, 12>>(out_value);
259410
```
260411
261-
# Serialization Examples
262-
The examples below follow the same structure: First writing to a buffer and then reading from it. Each example is littered with comments about the procedure, as well as what outcome is expected.
263-
264-
Writing the first 5 bits of an int to the buffer, then reading it back:
265-
```cpp
266-
// Create a writer, referencing the buffer and its size
267-
alignas(uint32_t) uint8_t buffer[4]; // Buffer must be a multiple of 4 bytes / 32 bits and 4-byte-aligned
268-
bit_writer writer(buffer, 4);
269-
270-
// Write the value
271-
uint32_t value = 27; // We can choose any value below 2^5. Otherwise we need more than 5 bits
272-
writer.serialize_bits(value, 5);
273-
274-
// Flush the writer's remaining state into the buffer
275-
uint32_t num_bits = writer.flush();
276-
277-
// Create a reader, referencing the buffer and bits written
278-
bit_reader reader(buffer, num_bits);
279-
280-
// Read the value back
281-
uint32_t out_value; // We don't have to initialize it yet
282-
reader.serialize_bits(out_value, 5); // out_value should now have a value of 27
283-
```
284-
285-
Writing a signed int to the buffer, within a range:
286-
```cpp
287-
// Create a writer, referencing the buffer and its size
288-
byte_buffer<4> buffer; // byte_bufer is just a wrapper for a 4-byte aligned buffer
289-
bit_writer writer(buffer);
290-
291-
// Write the value
292-
int32_t value = -45; // We can choose any value within the range below
293-
writer.serialize<int32_t>(value, -90, 40); // A lower and upper bound which the value will be quantized between
294-
295-
// Flush the writer's remaining state into the buffer
296-
uint32_t num_bits = writer.flush();
297-
298-
// Create a reader, referencing the buffer and bits written
299-
bit_reader reader(buffer, num_bits);
300-
301-
// Read the value back
302-
int32_t out_value; // We don't have to initialize it yet
303-
reader.serialize<int32_t>(out_value, -90, 40); // out_value should now have a value of -45
304-
```
305-
306-
Writing a c-style string into the buffer:
307-
```cpp
308-
// Create a writer, referencing the buffer and its size
309-
byte_buffer<32> buffer;
310-
bit_writer writer(buffer);
311-
312-
// Write the value
313-
const char* value = "Hello world!";
314-
writer.serialize<const char*>(value, 32U); // The second argument is the maximum size we expect the string to be
315-
316-
// Flush the writer's remaining state into the buffer
317-
uint32_t num_bits = writer.flush();
318-
319-
// Create a reader, referencing the buffer and bits written
320-
bit_reader reader(buffer, num_bits);
321-
322-
// Read the value back
323-
char out_value[32]; // Set the size to the max size
324-
reader.serialize<const char*>(out_value, 32U); // out_value should now contain "Hello world!\0"
325-
```
326-
327-
Writing a std::string into the buffer:
328-
```cpp
329-
// Create a writer, referencing the buffer and its size
330-
byte_buffer<32> buffer;
331-
bit_writer writer(buffer);
332-
333-
// Write the value
334-
std::string value = "Hello world!";
335-
writer.serialize<std::string>(value, 32U); // The second argument is the maximum size we expect the string to be
336-
337-
// Flush the writer's remaining state into the buffer
338-
uint32_t num_bits = writer.flush();
339-
340-
// Create a reader, referencing the buffer and bits written
341-
bit_reader reader(buffer, num_bits);
342-
343-
// Read the value back
344-
std::string out_value; // The string will be resized if the output doesn't fit
345-
reader.serialize<std::string>(out_value, 32U); // out_value should now contain "Hello world!"
346-
```
347-
348-
Writing a float into the buffer with a bounded range and precision:
349-
```cpp
350-
// Create a writer, referencing the buffer and its size
351-
byte_buffer<4> buffer;
352-
bit_writer writer(buffer);
353-
354-
// Write the value
355-
bounded_range range(1.0f, 4.0f, 1.0f / 128.0f); // Min, Max, Precision
356-
float value = 1.2345678f;
357-
writer.serialize<bounded_range>(range, value);
358-
359-
// Flush the writer's remaining state into the buffer
360-
uint32_t num_bits = writer.flush();
361-
362-
// Create a reader, referencing the buffer and bits written
363-
bit_reader reader(buffer, num_bits);
364-
365-
// Read the value back
366-
float out_value;
367-
reader.serialize<bounded_range>(range, out_value); // out_value should now be a value close to 1.2345678f
368-
```
369-
370-
These examples can also be seen in [`src/test/examples_test.cpp`](https://github.com/KredeGC/BitStream/tree/master/src/test/examples_test.cpp).
371-
372412
# Extensibility
373413
The library is made with extensibility in mind.
374414
The `bit_writer` and `bit_reader` use a template trait specialization of the given type to deduce how to serialize and deserialize the object.

0 commit comments

Comments
 (0)