Skip to content

Commit 196edb0

Browse files
committed
Let the brle library and the test program support C++14 and newer
The brle utility requires at least C++17. The makefile builds default with C++17 instead of C++20.
1 parent 8b4f511 commit 196edb0

File tree

5 files changed

+152
-17
lines changed

5 files changed

+152
-17
lines changed

README.md

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,24 @@
11
# A C++ library to compress or expand binary data using Run-Length Encoding
22

3-
This library implements a variant of the Run-Length Encoding compression method that is optimized for long sequences of ones or zeros.
3+
This library implements a variant of the Run-Length Encoding compression method that is optimized for sequences of ones or zeros.
44

55
The advantage of the RLE over other compression methods is that RLE can compress data in a single pass and does not require any buffering of the input or output data.
66
These properties can make a RLE good fit for applications that are tight on memory usage or require low latencies.
77
However due to simplicity of RLE the compression may not be as good as achieved by other compression methods.
88

99
## Features
1010

11-
* Optimized for long sequences of ones or zeros.
12-
* Robust; the decoder does not depend on previously decoded data.
11+
* Optimized for sequences of ones or zeros.
12+
* Robust
13+
* The encoder and decoder does not depend on previously processed data.
14+
* Fixed size RLE data [blocks](#Block-format).
1315
* Compress and expand data in a single pass.
1416
* No buffering of input data or output data.
1517

1618
## Requirements
1719

18-
A C++ compiler with a standard library that has the bit operation libaray `<bit>` is required.
19-
This library is part of the C++20 standard and comes whith MSVC 19.28 (VS 2019 16.8), GCC libstd++ 9 and Clang libc++ 9.
20-
You may need to enable C++20 for your compiler to it compile.
20+
A C++14 compiler when using the library or when building the test program.
21+
The [brle utility](#brle-utility) requires at least a C++17 compliant compiler.
2122

2223
## Goals
2324

@@ -36,7 +37,7 @@ You may need to enable C++20 for your compiler to it compile.
3637

3738
## Examples
3839

39-
Besides the following examples you can take a peek in the [test program](https://github.com/PG1003/brle/blob/main/tests/test.cpp) or the [brle utility](https://github.com/PG1003/brle/blob/main/util/brle.cpp) about the usage of the `brle` library.
40+
Besides the following examples you can take a peek in the sources of the[test program](https://github.com/PG1003/brle/blob/main/tests/test.cpp) and the [brle utility](https://github.com/PG1003/brle/blob/main/util/brle.cpp) about the usage of the `brle` library.
4041

4142
### Encode
4243

@@ -79,10 +80,10 @@ assert( data.size() == 4u );
7980
8081
## brle utility
8182
82-
There is an [implementation](https://github.com/PG1003/brle/blob/main/util/brle.cpp) for a commandline utility that uses this `brle` library.
83-
The utility can be used to test the efficiency of the compression for your use case or create a binary blobs that are going to be included in your application or firmware.
83+
The brle utility is an [implementation](https://github.com/PG1003/brle/blob/main/util/brle.cpp) for a commandline program that uses this `brle` library.
84+
The utility can be used to test the efficiency of the compression for your use case or to create binary blobs that are going to be included in your application or firmware.
8485
85-
You can build the utility with the provided makefile by running the following command;
86+
You can build the utility with the provided makefile by running the following make command;
8687
8788
```sh
8889
make brle

makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ CXX := g++
2929
LD := g++
3030

3131
# C++ flags
32-
CXXFLAGS := -std=c++20
32+
CXXFLAGS := -std=c++17
3333
# C/C++ flags
3434
CPPFLAGS := -Wall -Wextra -Wpedantic -O3
3535
# Extra include directories

src/brle.h

Lines changed: 129 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,14 @@
2525
#include <cstdint>
2626
#include <cassert>
2727
#include <algorithm>
28-
#include <bit>
2928
#include <limits>
3029
#include <iterator>
3130
#include <type_traits>
3231

32+
#if defined( __cpp_lib_bitops )
33+
#include <bit>
34+
#endif
35+
3336
namespace pg
3437
{
3538

@@ -85,6 +88,123 @@ static constexpr brle8 make_ones( int count )
8588
return static_cast< brle8 >( mode::ones | ( count - min_brle_len ) );
8689
}
8790

91+
#if defined( __cpp_lib_bitops )
92+
93+
//
94+
// Use perfect forwarding to create aliases for std::countr_zero and std::countr_one.
95+
//
96+
97+
template< typename T >
98+
auto countr_zero( T && val ) -> decltype( auto )
99+
{
100+
return std::countr_zero( std::forward< T >( val ) );
101+
}
102+
103+
template< typename T >
104+
auto countr_one( T && val ) -> decltype( auto )
105+
{
106+
return std::countr_one( std::forward< T >( val ) );
107+
}
108+
109+
#else
110+
111+
//
112+
// Surrogates for the std::countr_zero std::countr_one functions when NOT compiling with C++20.
113+
//
114+
// Inspired by:
115+
// https://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup
116+
// http://www.hakank.org/comb/debruijn_arb.cgi
117+
//
118+
119+
template< typename T >
120+
constexpr int countr_zero( const T val )
121+
{
122+
static_assert( std::is_unsigned< T >::value, "expected an unsigned value" );
123+
124+
if( val )
125+
{
126+
const uint64_t de_bruin_64 = 0x003B1234F32FD42B7;
127+
const int lookup[ 64 ] = { 0, 1, 50, 2, 12, 51, 19, 3,
128+
16, 13, 52, 36, 32, 20, 4, 26,
129+
48, 17, 14, 24, 46, 53, 55, 37,
130+
9, 33, 21, 57, 60, 5, 27, 39,
131+
63, 49, 11, 18, 15, 35, 31, 25,
132+
47, 23, 45, 54, 8, 56, 59, 38,
133+
62, 10, 34, 30, 22, 44, 7, 58,
134+
61, 29, 43, 6, 28, 42, 41, 40 };
135+
136+
const uint64_t hash = ( val & -val ) * de_bruin_64;
137+
const uint64_t index = hash >> 58;
138+
139+
return lookup[ index ];
140+
}
141+
142+
return 64;
143+
}
144+
145+
template<>
146+
constexpr int countr_zero< uint8_t >( const uint8_t val )
147+
{
148+
if( val )
149+
{
150+
const uint8_t de_bruin_8 = 0x1D;
151+
const int lookup[ 8 ] = { 0, 1, 6, 2, 7, 5, 4, 3 };
152+
153+
const uint8_t hash = ( val & -val ) * de_bruin_8;
154+
const uint8_t index = hash >> 5;
155+
156+
return lookup[ index ];
157+
}
158+
159+
return 8;
160+
}
161+
162+
template<>
163+
constexpr int countr_zero< uint16_t >( const uint16_t val )
164+
{
165+
if( val )
166+
{
167+
const uint32_t de_bruin_16 = 0x0D2F;
168+
const int lookup[ 16 ] = { 0, 1, 8, 2, 6, 9, 3, 11,
169+
15, 7, 5, 10, 14, 4, 13, 12 };
170+
171+
const uint16_t hash = ( val & -val ) * de_bruin_16;
172+
const uint16_t index = hash >> 12;
173+
174+
return lookup[ index ];
175+
}
176+
177+
return 16;
178+
}
179+
180+
template<>
181+
constexpr int countr_zero< uint32_t >( const uint32_t val )
182+
{
183+
if( val )
184+
{
185+
const uint32_t de_bruin_32 = 0x077CB531;
186+
const int lookup[ 32 ] = { 0, 1, 28, 2, 29, 14, 24, 3,
187+
30, 22, 20, 15, 25, 17, 4, 8,
188+
31, 27, 13, 23, 21, 19, 16, 7,
189+
26, 12, 18, 6, 11, 5, 10, 9 };
190+
191+
const uint32_t hash = ( val & -val ) * de_bruin_32;
192+
const uint32_t index = hash >> 27;
193+
194+
return lookup[ index ];
195+
}
196+
197+
return 32;
198+
}
199+
200+
template< typename T >
201+
constexpr int countr_one( T val )
202+
{
203+
return countr_zero( static_cast< T >( ~val ) );
204+
}
205+
206+
#endif
207+
88208
}
89209

90210

@@ -93,7 +213,8 @@ auto encode( InputIt input, InputIt last, OutputIt output ) -> OutputIt
93213
{
94214
using InputValueT = typename std::iterator_traits< InputIt >::value_type;
95215

96-
static_assert( std::is_unsigned< InputValueT >::value );
216+
static_assert( std::is_unsigned< InputValueT >::value,
217+
"expected an input iterator that returns an unsigned value when dereferenced" );
97218

98219
constexpr int data_bits = std::numeric_limits< InputValueT >::digits;
99220

@@ -113,8 +234,8 @@ auto encode( InputIt input, InputIt last, OutputIt output ) -> OutputIt
113234
}
114235

115236
int count = 0;
116-
const auto zeros = std::min( std::countr_zero( bits ), bit_count );
117-
const auto ones = std::min( std::countr_one( bits ), bit_count );
237+
const auto zeros = std::min( detail::countr_zero( bits ), bit_count );
238+
const auto ones = std::min( detail::countr_one( bits ), bit_count );
118239

119240
switch( state )
120241
{
@@ -213,8 +334,10 @@ auto encode( InputIt input, InputIt last, OutputIt output ) -> OutputIt
213334
template< typename InputIt, typename OutputIt, typename OutputValueT = typename std::iterator_traits< OutputIt >::value_type >
214335
auto decode( InputIt input, InputIt last, OutputIt output ) -> OutputIt
215336
{
216-
static_assert( std::is_same< typename std::iterator_traits< InputIt >::value_type, brle8 >::value );
217-
static_assert( std::is_unsigned< OutputValueT >::value );
337+
static_assert( std::is_same< typename std::iterator_traits< InputIt >::value_type, brle8 >::value,
338+
"expected an input iterator that returns brle8 like type when dereferenced" );
339+
static_assert( std::is_unsigned< OutputValueT >::value,
340+
"expected an unsigned value type as output" );
218341

219342
constexpr int data_bits = std::numeric_limits< OutputValueT >::digits;
220343
constexpr OutputValueT data_mask = std::numeric_limits< OutputValueT >::max();

tests/test.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include <brle.h>
2+
#include <vector>
23
#include <cstring>
34
#include <iostream>
45
#include <iterator>

util/brle.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,11 @@ struct binary_input_file_iterator
104104
return file == other.file && std::ftell( file ) == std::ftell( other.file );
105105
}
106106

107+
bool operator!=( const binary_input_file_iterator & other ) const
108+
{
109+
return !operator==( other );
110+
}
111+
107112
T & operator*() { return value; }
108113
binary_input_file_iterator * operator->() const { return &value; }
109114
binary_input_file_iterator & operator++() { next(); return *this; }
@@ -169,6 +174,11 @@ struct binary_output_file_iterator
169174
return file == other.file && std::ftell( file ) == std::ftell( other.file );
170175
}
171176

177+
bool operator!=( const binary_output_file_iterator & other ) const
178+
{
179+
return !operator==( other );
180+
}
181+
172182
binary_output_file_iterator & operator*() { return *this; }
173183
binary_output_file_iterator & operator++() { return *this; }
174184
binary_output_file_iterator operator++( int ) { return *this; }

0 commit comments

Comments
 (0)