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>
|
||||
struct PrimitiveTypeWriter<
|
||||
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)
|
||||
{
|
||||
stream.write<ValueT>(value, sizeof(ValueT) * 8);
|
||||
stream.write<ValueT>(value, bitsizeof<ValueT>::value);
|
||||
}
|
||||
};
|
||||
|
||||
template <typename ValueT>
|
||||
struct PrimitiveTypeReader<
|
||||
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)
|
||||
{
|
||||
value = stream.read<ValueT>(sizeof(ValueT) * 8);
|
||||
value = stream.read<ValueT>(bitsizeof<ValueT>::value);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include "ki/util/BitTypes.h"
|
||||
|
||||
#define KI_BITSTREAM_DEFAULT_BUFFER_SIZE 0x2000
|
||||
|
||||
|
@ -73,9 +74,9 @@ namespace ki
|
|||
*/
|
||||
template <
|
||||
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;
|
||||
|
||||
|
@ -116,9 +117,9 @@ namespace ki
|
|||
*/
|
||||
template <
|
||||
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
|
||||
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_U64 0x1211100F0E0D0C0BL
|
||||
|
||||
#define KI_TEST_WRITE_BUI(n) bit_stream->write(KI_TEST_BITSTREAM_BUI##n, n)
|
||||
#define KI_TEST_READ_BUI(n) bit_stream->read<uint8_t>(n) == KI_TEST_BITSTREAM_BUI##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<bui<n>>() == KI_TEST_BITSTREAM_BUI##n
|
||||
|
||||
using namespace ki;
|
||||
|
||||
|
@ -117,11 +117,11 @@ TEST_CASE("BitStream Functionality", "[bit-stream]")
|
|||
SECTION("Writing values with a size greater than 8 bits")
|
||||
{
|
||||
// Write some values
|
||||
bit_stream->write<uint8_t>(KI_TEST_BITSTREAM_U8, 8);
|
||||
bit_stream->write<uint16_t>(KI_TEST_BITSTREAM_U16, 16);
|
||||
bit_stream->write<uint32_t>(KI_TEST_BITSTREAM_U24, 24);
|
||||
bit_stream->write<uint32_t>(KI_TEST_BITSTREAM_U32, 32);
|
||||
bit_stream->write<uint64_t>(KI_TEST_BITSTREAM_U64, 64);
|
||||
bit_stream->write<uint8_t>(KI_TEST_BITSTREAM_U8);
|
||||
bit_stream->write<uint16_t>(KI_TEST_BITSTREAM_U16);
|
||||
bit_stream->write<bui<24>>(KI_TEST_BITSTREAM_U24);
|
||||
bit_stream->write<uint32_t>(KI_TEST_BITSTREAM_U32);
|
||||
bit_stream->write<uint64_t>(KI_TEST_BITSTREAM_U64);
|
||||
|
||||
// Make sure tell is reporting the right position
|
||||
auto position = bit_stream->tell();
|
||||
|
@ -197,15 +197,15 @@ TEST_CASE("BitStream Functionality", "[bit-stream]")
|
|||
sample.read((char *)bit_stream->data(), size);
|
||||
|
||||
// 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();
|
||||
if (bit_stream->read<uint16_t>(16) != KI_TEST_BITSTREAM_U16)
|
||||
if (bit_stream->read<uint16_t>() != KI_TEST_BITSTREAM_U16)
|
||||
FAIL();
|
||||
if (bit_stream->read<uint32_t>(24) != KI_TEST_BITSTREAM_U24)
|
||||
if (bit_stream->read<bui<24>>() != KI_TEST_BITSTREAM_U24)
|
||||
FAIL();
|
||||
if (bit_stream->read<uint32_t>(32) != KI_TEST_BITSTREAM_U32)
|
||||
if (bit_stream->read<uint32_t>() != KI_TEST_BITSTREAM_U32)
|
||||
FAIL();
|
||||
if (bit_stream->read<uint64_t>(64) != KI_TEST_BITSTREAM_U64)
|
||||
if (bit_stream->read<uint64_t>() != KI_TEST_BITSTREAM_U64)
|
||||
FAIL();
|
||||
|
||||
SUCCEED();
|
||||
|
|
Loading…
Reference in New Issue