Skip to content

Commit 76517e9

Browse files
committed
Add 32 and 64 bit float readers.
1 parent d1b4387 commit 76517e9

File tree

4 files changed

+137
-10
lines changed

4 files changed

+137
-10
lines changed

bitreader/include/bitreader/bitreader-utils.hpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,6 @@ namespace brcpp
2222
template<> struct integral_by_size<4>: id_t<uint32_t> {};
2323
template<> struct integral_by_size<8>: id_t<uint64_t> {};
2424

25-
template<typename Enum>
26-
using fitting_integral = typename integral_by_size<sizeof(Enum)>::type;
27-
2825
struct binary_codec_base {};
2926

3027
template<typename T>
@@ -40,6 +37,12 @@ namespace brcpp
4037
using std::signed_integral;
4138
using std::floating_point;
4239

40+
template<typename T>
41+
concept bit_readable = integral<T> || floating_point<T>;
42+
43+
template<bit_readable T>
44+
using fitting_integral = typename integral_by_size<sizeof(T)>::type;
45+
4346
template<typename T>
4447
struct bit_read_helper {};
4548

bitreader/include/bitreader/bitreader.hpp

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -80,12 +80,14 @@ namespace brcpp {
8080
* @param bits Number of bits to read
8181
* @return The data read from the stream
8282
*/
83-
template<integral T>
83+
template<bit_readable T>
8484
T read(size_t bits)
8585
{
86+
using FT = fitting_integral<T>;
8687
_validate_read_dynamic<T>(bits);
87-
T ret = T(0);
88-
_read(_state, bits, ret);
88+
T ret = zero<T>;
89+
auto& raw = reinterpret_cast<FT&>(ret);
90+
_read(_state, bits, raw);
8991
return _sign_extend(ret, bits);
9092
}
9193

@@ -158,6 +160,13 @@ namespace brcpp {
158160
return raw;
159161
}
160162

163+
//----------------------------------------------------------------------
164+
template<floating_point T>
165+
T _sign_extend(T raw, size_t bits)
166+
{
167+
return raw;
168+
}
169+
161170
//----------------------------------------------------------------------
162171
void _next(internal_state& state) const
163172
{
@@ -277,8 +286,10 @@ namespace brcpp {
277286
template <typename T>
278287
void _validate_read_dynamic(size_t size) const
279288
{
280-
assert(size <= bit_read_helper<T>::max_bits);
281-
assert(size >= bit_read_helper<T>::min_bits);
289+
if (size < bit_read_helper<T>::min_bits || size > bit_read_helper<T>::max_bits)
290+
{
291+
throw std::runtime_error("Invalid read size");
292+
}
282293
}
283294

284295
internal_state _state;

bitreader/include/bitreader/bitwriter.hpp

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,15 +52,18 @@ namespace brcpp {
5252
}
5353

5454
//----------------------------------------------------------------------
55-
template<integral T>
55+
template<bit_readable T>
5656
void write(T data, size_t bits)
5757
{
5858
size_t written = 0;
5959
size_t to_write = bits;
6060

61+
using FT = fitting_integral<T>;
62+
const auto bit_data = *reinterpret_cast<const FT*>(&data);
63+
6164
while (written < bits) {
6265
size_t post = std::min<uint64_t>(_state.avail, to_write);
63-
T portion = (data >> (bits - written - post)) & _mask<T>(post);
66+
FT portion = (bit_data >> (bits - written - post)) & _mask<FT>(post);
6467
size_t diff = _state.avail - post;
6568
_state.buffer |= static_cast<internal_state::buffer_type>(portion << diff);
6669
_state.avail -= post;

bitreader/test/bitreader_gtest.cpp

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -416,3 +416,113 @@ TEST(bitreaderTest, align_to_non_byte_boundary)
416416
EXPECT_EQ(5, br.position());
417417
EXPECT_EQ(11, br.available());
418418
}
419+
420+
//------------------------------------------------------------------------------
421+
TEST(bitreaderTest, read_float_aligned)
422+
{
423+
const uint8_t data[] = {0x41, 0x48, 0xF5, 0xC3};
424+
auto source = std::make_shared<source_t>(data, sizeof(data));
425+
bitreader<source_t> br(source);
426+
427+
const uint32_t raw_expected = 0x4148F5C3;
428+
const auto expected = *reinterpret_cast<const float*>(&raw_expected);
429+
EXPECT_EQ(expected, br.read<float>(32));
430+
EXPECT_EQ(32, br.position());
431+
EXPECT_EQ(0, br.available());
432+
}
433+
434+
//------------------------------------------------------------------------------
435+
TEST(bitreaderTest, read_double_aligned)
436+
{
437+
const uint8_t data[] = {0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57, 0x62};
438+
auto source = std::make_shared<source_t>(data, sizeof(data));
439+
bitreader<source_t> br(source);
440+
441+
const uint64_t raw_expected = 0x4005BF0A8B145762;
442+
const auto expected = *reinterpret_cast<const double*>(&raw_expected);
443+
EXPECT_EQ(expected, br.read<double>(64));
444+
EXPECT_EQ(64, br.position());
445+
EXPECT_EQ(0, br.available());
446+
}
447+
448+
//------------------------------------------------------------------------------
449+
TEST(bitreaderTest, read_float_unaligned)
450+
{
451+
const uint8_t data[] = {0x10, 0x20, 0x30, 0x40, 0x50};
452+
auto source = std::make_shared<source_t>(data, sizeof(data));
453+
bitreader<source_t> br(source);
454+
455+
br.skip(4);
456+
457+
const uint32_t raw_expected = 0x02030405;
458+
const auto expected = *reinterpret_cast<const float*>(&raw_expected);
459+
EXPECT_EQ(expected, br.read<float>(32)); // exact eq
460+
EXPECT_EQ(36, br.position());
461+
EXPECT_EQ(4, br.available());
462+
}
463+
464+
//------------------------------------------------------------------------------
465+
TEST(bitreaderTest, read_double_unaligned)
466+
{
467+
const uint8_t data[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90};
468+
auto source = std::make_shared<source_t>(data, sizeof(data));
469+
bitreader<source_t> br(source);
470+
471+
br.skip(4);
472+
473+
const uint64_t raw_expected = 0x0203'0405'0607'0809;
474+
const auto expected = *reinterpret_cast<const double*>(&raw_expected);
475+
EXPECT_EQ(expected, br.read<double>(64));
476+
EXPECT_EQ(68, br.position());
477+
EXPECT_EQ(4, br.available());
478+
}
479+
480+
//------------------------------------------------------------------------------
481+
TEST(bitreaderTest, read_double_wrong_size)
482+
{
483+
const uint8_t data[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90};
484+
auto source = std::make_shared<source_t>(data, sizeof(data));
485+
bitreader<source_t> br(source);
486+
487+
br.skip(4);
488+
489+
EXPECT_THROW(br.read<double>(63), std::exception); // wrong size
490+
EXPECT_EQ(4, br.position());
491+
}
492+
493+
//------------------------------------------------------------------------------
494+
TEST(bitreaderTest, read_float_wrong_size)
495+
{
496+
const uint8_t data[] = {0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90};
497+
auto source = std::make_shared<source_t>(data, sizeof(data));
498+
bitreader<source_t> br(source);
499+
500+
br.skip(4);
501+
502+
EXPECT_THROW(br.read<float>(31), std::exception); // wrong size
503+
EXPECT_EQ(4, br.position());
504+
}
505+
506+
//------------------------------------------------------------------------------
507+
TEST(bitreaderTest, read_float_insufficient_data)
508+
{
509+
const uint8_t data[] = {0x41, 0x48, 0xF5}; // Only 3 bytes for float
510+
auto source = std::make_shared<source_t>(data, sizeof(data));
511+
bitreader<source_t> br(source);
512+
513+
EXPECT_THROW(br.read<float>(32), std::exception);
514+
EXPECT_EQ(0, br.position());
515+
EXPECT_EQ(24, br.available());
516+
}
517+
518+
//------------------------------------------------------------------------------
519+
TEST(bitreaderTest, read_double_insufficient_data)
520+
{
521+
const uint8_t data[] = {0x40, 0x05, 0xBF, 0x0A, 0x8B, 0x14, 0x57}; // Only 7 bytes for double
522+
auto source = std::make_shared<source_t>(data, sizeof(data));
523+
bitreader<source_t> br(source);
524+
525+
EXPECT_THROW(br.read<double>(64), std::exception);
526+
EXPECT_EQ(0, br.position());
527+
EXPECT_EQ(56, br.available());
528+
}

0 commit comments

Comments
 (0)