mirror of https://github.com/SeanOMik/libki.git
pclass: Implement BitInteger and other bit utilities
This commit is contained in:
parent
73608ce23c
commit
8f5d31d071
|
@ -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);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 {};
|
||||||
|
}
|
|
@ -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();
|
||||||
|
|
Loading…
Reference in New Issue