pclass: Implement BitInteger and other bit utilities

This commit is contained in:
Joshua Scott 2018-10-26 14:42:00 +01:00
parent 73608ce23c
commit 8f5d31d071
4 changed files with 230 additions and 20 deletions

View File

@ -8,24 +8,24 @@ namespace pclass
template <typename ValueT> template <typename ValueT>
struct PrimitiveTypeWriter< struct PrimitiveTypeWriter<
ValueT, ValueT,
typename std::enable_if<std::is_integral<ValueT>::value>::type typename std::enable_if<is_integral<ValueT>::value>::type
> >
{ {
static void write_to(BitStream &stream, const ValueT &value) static void write_to(BitStream &stream, const ValueT &value)
{ {
stream.write<ValueT>(value, sizeof(ValueT) * 8); stream.write<ValueT>(value, bitsizeof<ValueT>::value);
} }
}; };
template <typename ValueT> template <typename ValueT>
struct PrimitiveTypeReader< struct PrimitiveTypeReader<
ValueT, ValueT,
typename std::enable_if<std::is_integral<ValueT>::value>::type typename std::enable_if<is_integral<ValueT>::value>::type
> >
{ {
static void read_from(BitStream &stream, ValueT &value) static void read_from(BitStream &stream, ValueT &value)
{ {
value = stream.read<ValueT>(sizeof(ValueT) * 8); value = stream.read<ValueT>(bitsizeof<ValueT>::value);
} }
}; };
} }

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <cstdint> #include <cstdint>
#include <type_traits> #include <type_traits>
#include "ki/util/BitTypes.h"
#define KI_BITSTREAM_DEFAULT_BUFFER_SIZE 0x2000 #define KI_BITSTREAM_DEFAULT_BUFFER_SIZE 0x2000
@ -73,9 +74,9 @@ namespace ki
*/ */
template < template <
typename IntegerT, typename IntegerT,
typename = std::enable_if<std::is_integral<IntegerT>::value> typename = std::enable_if<is_integral<IntegerT>::value>
> >
IntegerT read(const uint8_t bits) IntegerT read(const uint8_t bits = bitsizeof<IntegerT>::value)
{ {
IntegerT value = 0; IntegerT value = 0;
@ -116,9 +117,9 @@ namespace ki
*/ */
template < template <
typename IntegerT, typename IntegerT,
typename = std::enable_if<std::is_integral<IntegerT>::value> typename = std::enable_if<is_integral<IntegerT>::value>
> >
void write(IntegerT value, const uint8_t bits) void write(IntegerT value, const uint8_t bits = bitsizeof<IntegerT>::value)
{ {
// Iterate until we've written all of the bits // Iterate until we've written all of the bits
auto unwritten_bits = bits; auto unwritten_bits = bits;

209
include/ki/util/BitTypes.h Normal file
View File

@ -0,0 +1,209 @@
#pragma once
#include <cstdint>
#include <type_traits>
namespace ki
{
/**
* A helper utility that provides the most suitable primitive type
* to store an N bit integer with the appropriate signedness.
*/
template <uint8_t N>
struct bits
{
// Do not allow 0 bits.
static_assert(N > 0, "N must be greater than 0.");
// Do not allow a bit length > 64 as there is no
// type to adequately represent these values.
static_assert(N <= 64, "N must be less than or equal to 64.");
/**
* The integer type that can most efficiently store N bits
* with signedness.
*/
using int_type = typename std::conditional<
N <= 8,
int8_t,
typename std::conditional<
N <= 16,
int16_t,
typename std::conditional<
N <= 32,
int32_t,
int64_t
>::type
>::type
>::type;
/**
* The integer type that can most efficiently store N bits
* without signedness.
*/
using uint_type = typename std::conditional<
N <= 8,
uint8_t,
typename std::conditional<
N <= 16,
uint16_t,
typename std::conditional<
N <= 32,
uint32_t,
uint64_t
>::type
>::type
>::type;
};
/**
* Represents an integer of N bits.
*/
template <uint8_t N, bool Unsigned>
struct BitInteger
{
/**
* The type used to internally store the N-bit integer.
*/
using type = typename std::conditional<
Unsigned,
typename bits<N>::uint_type,
typename bits<N>::int_type
>::type;
BitInteger()
{
m_value = 0;
}
BitInteger(BitInteger<N, Unsigned> &cp)
{
m_value = cp.m_value;
}
BitInteger(const type value)
{
m_value = value;
}
BitInteger<N, Unsigned> &operator =(const type rhs)
{
m_value = rhs;
return *this;
}
BitInteger<N, Unsigned> &operator +=(const type rhs)
{
m_value += rhs;
return *this;
}
BitInteger<N, Unsigned> &operator -=(const type rhs)
{
m_value -= rhs;
return *this;
}
BitInteger<N, Unsigned> &operator *=(const type rhs)
{
m_value *= rhs;
return *this;
}
BitInteger<N, Unsigned> &operator /=(const type rhs)
{
m_value /= rhs;
return *this;
}
BitInteger<N, Unsigned> &operator |=(const type rhs)
{
m_value |= rhs;
return *this;
}
BitInteger<N, Unsigned> &operator &=(const type rhs)
{
m_value &= rhs;
return *this;
}
BitInteger<N, Unsigned> &operator ++()
{
m_value += 1;
return *this;
}
BitInteger<N, Unsigned> &operator --()
{
m_value -= 1;
return *this;
}
BitInteger<N, Unsigned> operator ++(int increment)
{
auto copy(*this);
++(*this);
return copy;
}
BitInteger<N, Unsigned> operator --(int increment)
{
auto copy(*this);
--(*this);
return copy;
}
operator type() const
{
return m_value;
}
private:
type m_value : N;
};
/**
* Represents a signed integer of N bits.
*/
template <uint8_t N>
using bi = BitInteger<N, false>;
/**
* Represents an unsigned integer of N bits.
*/
template <uint8_t N>
using bui = BitInteger<N, true>;
/**
* A utility to calculate the bitsize of a type.
*/
template <typename T, typename Enable = void>
struct bitsizeof
{
/**
* The number of bits a type will occupy if written to a BitStream.
* This does not reflect the size of the type in memory.
*/
static constexpr size_t value = sizeof(T) * 8;
};
template <uint8_t N, bool Unsigned>
struct bitsizeof<BitInteger<N, Unsigned>>
{
static constexpr size_t value = N;
};
/**
* Determines whether a given type is integral.
*/
template <typename T, typename Enable = void>
struct is_integral : std::false_type {};
template <typename T>
struct is_integral<
T,
typename std::enable_if<std::is_integral<T>::value>::type
> : std::true_type {};
template <uint8_t N, bool Unsigned>
struct is_integral<BitInteger<N, Unsigned>> : std::true_type {};
}

View File

@ -18,8 +18,8 @@
#define KI_TEST_BITSTREAM_U32 0x0A090807 #define KI_TEST_BITSTREAM_U32 0x0A090807
#define KI_TEST_BITSTREAM_U64 0x1211100F0E0D0C0BL #define KI_TEST_BITSTREAM_U64 0x1211100F0E0D0C0BL
#define KI_TEST_WRITE_BUI(n) bit_stream->write(KI_TEST_BITSTREAM_BUI##n, n) #define KI_TEST_WRITE_BUI(n) bit_stream->write<bui<n>>(KI_TEST_BITSTREAM_BUI##n)
#define KI_TEST_READ_BUI(n) bit_stream->read<uint8_t>(n) == KI_TEST_BITSTREAM_BUI##n #define KI_TEST_READ_BUI(n) bit_stream->read<bui<n>>() == KI_TEST_BITSTREAM_BUI##n
using namespace ki; using namespace ki;
@ -117,11 +117,11 @@ TEST_CASE("BitStream Functionality", "[bit-stream]")
SECTION("Writing values with a size greater than 8 bits") SECTION("Writing values with a size greater than 8 bits")
{ {
// Write some values // Write some values
bit_stream->write<uint8_t>(KI_TEST_BITSTREAM_U8, 8); bit_stream->write<uint8_t>(KI_TEST_BITSTREAM_U8);
bit_stream->write<uint16_t>(KI_TEST_BITSTREAM_U16, 16); bit_stream->write<uint16_t>(KI_TEST_BITSTREAM_U16);
bit_stream->write<uint32_t>(KI_TEST_BITSTREAM_U24, 24); bit_stream->write<bui<24>>(KI_TEST_BITSTREAM_U24);
bit_stream->write<uint32_t>(KI_TEST_BITSTREAM_U32, 32); bit_stream->write<uint32_t>(KI_TEST_BITSTREAM_U32);
bit_stream->write<uint64_t>(KI_TEST_BITSTREAM_U64, 64); bit_stream->write<uint64_t>(KI_TEST_BITSTREAM_U64);
// Make sure tell is reporting the right position // Make sure tell is reporting the right position
auto position = bit_stream->tell(); auto position = bit_stream->tell();
@ -197,15 +197,15 @@ TEST_CASE("BitStream Functionality", "[bit-stream]")
sample.read((char *)bit_stream->data(), size); sample.read((char *)bit_stream->data(), size);
// Read the values and check they are what we are expecting // Read the values and check they are what we are expecting
if (bit_stream->read<uint8_t>(8) != KI_TEST_BITSTREAM_U8) if (bit_stream->read<uint8_t>() != KI_TEST_BITSTREAM_U8)
FAIL(); FAIL();
if (bit_stream->read<uint16_t>(16) != KI_TEST_BITSTREAM_U16) if (bit_stream->read<uint16_t>() != KI_TEST_BITSTREAM_U16)
FAIL(); FAIL();
if (bit_stream->read<uint32_t>(24) != KI_TEST_BITSTREAM_U24) if (bit_stream->read<bui<24>>() != KI_TEST_BITSTREAM_U24)
FAIL(); FAIL();
if (bit_stream->read<uint32_t>(32) != KI_TEST_BITSTREAM_U32) if (bit_stream->read<uint32_t>() != KI_TEST_BITSTREAM_U32)
FAIL(); FAIL();
if (bit_stream->read<uint64_t>(64) != KI_TEST_BITSTREAM_U64) if (bit_stream->read<uint64_t>() != KI_TEST_BITSTREAM_U64)
FAIL(); FAIL();
SUCCEED(); SUCCEED();