Created
September 27, 2016 09:58
-
-
Save yohhoy/0400225073840ef049b13cce0c03baa4 to your computer and use it in GitHub Desktop.
lightweight bitstream parsing library
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 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