Skip to content

Instantly share code, notes, and snippets.

@yohhoy
Created September 27, 2016 09:58
Show Gist options
  • Save yohhoy/0400225073840ef049b13cce0c03baa4 to your computer and use it in GitHub Desktop.
Save yohhoy/0400225073840ef049b13cce0c03baa4 to your computer and use it in GitHub Desktop.
lightweight bitstream parsing library
/*
* lwbs.hpp - lightweight bitstream parsing library
*
* Copyright (c) 2016 yohhoy
* Distributed under the Boost Software License, Version 1.0.
* (See copy at http://www.boost.org/LICENSE_1_0.txt)
*
* Requirement:
* - C++11 support
* - (u)int64_t support
*/
#ifndef LWBS_HPP_INCLUDED_
#define LWBS_HPP_INCLUDED_
#endif
#include <algorithm>
#include <cassert>
#include <cstdint>
#include <exception>
#include <limits>
namespace lwbs {
struct buffer_overrun : std::exception {};
/*
* bitstream parser
*/
class bs_parser {
const uint8_t* head_; // buffer head
const uint8_t* tail_; // buffer tail
const uint8_t* curr_; // byte cursor
size_t bpos_; // remain bits on curr_
public:
bs_parser(const uint8_t* data, size_t nbyte)
: head_(data), tail_(data + nbyte), curr_(data), bpos_(8)
{ assert(data); assert(0 < nbyte); }
bs_parser(const char* data, size_t nbyte)
: bs_parser(reinterpret_cast<const uint8_t*>(data)), nbyte)
{}
// rewind position
void rewind() {
curr_ = head_;
bpos_ = 8;
}
// byte aligned?
bool aligned() const { return (bpos_ == 8 || bpos_ == 0); }
// read N bits
#define READBIT(UIntType, FName) \
bool try_##FName(size_t len, UIntType& v) { \
assert(0 < len && len <= sizeof(UIntType) * 8); \
UIntType acc = 0; \
while (0 < len) { \
size_t n = (std::min)(len, (size_t)8); \
if (bpos_ == 0) { \
if (curr_ == tail_) \
return false; \
++curr_; \
bpos_ = 8; \
} \
n = (std::min)(n, bpos_); \
if (curr_ == tail_) \
return false; \
acc <<= n; \
acc |= (*curr_ >> (bpos_ - n)) & ((static_cast<UIntType>(1) << n) - 1); \
len -= n; \
bpos_ -= n; \
} \
v = acc; \
return true; \
} \
UIntType FName(size_t n) \
{ \
UIntType v; \
if (!try_##FName(n, v)) \
throw buffer_overrun(); \
return v; \
}
READBIT(unsigned int, read_bits) // 16+ bit (practically 32bit)
READBIT(uint32_t, read_bits32)
READBIT(uint64_t, read_bits64)
#undef READBIT
// Standard(ISO/IEC, ITU-T, etc.) bit operation
uint64_t bslbf(size_t n) { return read_bits64(n); }
uint64_t uimsbf(size_t n) { return read_bits64(n); }
};
} // namespace lwbs
#if 0
#include <stdio.h>
int main()
{
{
const char data[] = { 0x12, 0x34, 0x56, 0x78 };
lwbs::bs_parser bs(data, 4);
unsigned int v1 = bs.read_bits(6); // ok
assert(v1 == 0b000100); assert(!bs.aligned());
unsigned int v2 = bs.read_bits(6); // ok
assert(v2 == 0b100011); assert(!bs.aligned());
unsigned int v3 = bs.read_bits(4); // ok
assert(v3 == 0b0100); assert( bs.aligned());
unsigned int v4;
bool r4 = bs.try_read_bits(12, v4); // true
assert(r4 && v4 == 0b010101100111);
unsigned int t;
bool r5 = bs.try_read_bits(5, t); // false
assert(!r5);
bool r6 = bs.try_read_bits(1, t); // false
assert(!r6);
}
{
const uint8_t data[] = { 0b11010101, 0b10110001 };
lwbs::bs_parser bs(data, 2);
try {
uint64_t v1 = bs.read_bits64(14); // ok
assert(v1 == 0b11010101101100);
bs.read_bits32(32); // throw
assert(!"UNREACHED");
} catch (const lwbs::buffer_overrun&) {
assert(!!"PASSED");
} catch (...) { assert(!"UNREACHED"); }
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment