Skip to content

Commit

Permalink
working on it...
Browse files Browse the repository at this point in the history
  • Loading branch information
mortenvp committed Oct 27, 2017
1 parent 5baba9c commit 84afe84
Show file tree
Hide file tree
Showing 8 changed files with 201 additions and 30 deletions.
31 changes: 22 additions & 9 deletions README.rst
Expand Up @@ -15,15 +15,19 @@ Usage

bitter provides 4 different readers/writers::

bitter::lsb0_writer<DataType, Fields...>();
bitter::lsb0_reader<DataType, Fields...>();
bitter::msb0_writer<DataType, Fields...>();
bitter::msb0_reader<DataType, Fields...>();

Where ``DataType`` is a bitter type e.g. ``u8``, ``u16``,
``u24`` etc. (u8 is short for unsigned 8 bit). ``Fields...`` is a variadic
bitter::lsb0_writer<Type, Fields...>();
bitter::lsb0_reader<Type, Fields...>();
bitter::msb0_writer<Type, Fields...>();
bitter::msb0_reader<Type, Fields...>();

Where ``Type`` is a unsigned integer type e.g. ``uint8_t``, ``uint16_t``,
``uint32_t`` etc. (``uint8_t`` is short for unsigned 8 bit, these types
are defined in the ``<ctdint>`` header). ``Fields...`` is a variadic
template argument specifying the different bit fields. The curiously
looking ``lsb0`` and ``msb0`` specifies the "bit numbering" used.
looking ``lsb0`` and ``msb0`` specifies the "bit numbering" used. In some
rare cases you may want to read e.g. 24 bits or 40 bits for which no
standard integer types are defined, however also for those you can use
bitter see

To use bitter for reading/writing bit fields you need to first decide on
what bit numbering scheme to use - if you never heard about this concept
Expand Down Expand Up @@ -227,7 +231,7 @@ inside the byte.

If on the other hand we use the ``msb0_reader`` the example would be::

auto reader = bitter::msb0_reader<bitter::u8, 1, 2, 3, 2>(0xdeadbeef);
auto reader = bitter::msb0_reader<uint8_t, 1, 2, 3, 2>(0xdeadbeef);

We would have the following layout of the four fields inside the byte::

Expand All @@ -240,6 +244,15 @@ We would have the following layout of the four fields inside the byte::
+-----------+ bit


Generic sized bit fields
------------------------

In some cases you may want to read/write an odd number of bytes e.g. 5
corresponding to 40 bits from//to a value. In that case you can use
bitter's generic data types (defined in ``src/bitter/types.hpp``) such
as ``u8``, ``u16``, ``u24``, ``u32``, ``u40`` etc.


Byte endianness
---------------

Expand Down
18 changes: 11 additions & 7 deletions src/bitter/reader.hpp
Expand Up @@ -12,6 +12,7 @@
#include "msb0.hpp"
#include "bit_field.hpp"
#include "types.hpp"
#include "to_type.hpp"

#include <cstdint>
#include <vector>
Expand All @@ -22,22 +23,25 @@ namespace bitter
{
/// @brief Reader class used for reading the content
/// of the value parsed to the reader at initialization
template<typename DataType, typename BitNumbering, uint32_t... Sizes>
template<typename Type, typename BitNumbering, uint32_t... Sizes>
class reader
{
public:

// Get the bitter type
using bitter_type = to_type<Type>;

/// Small alias for the bit_field
template<uint32_t Index>
using bit_field_type =
bit_field<typename DataType::type, field_size_in_bits<Index, Sizes...>()>;
bit_field<typename bitter_type::type, field_size_in_bits<Index, Sizes...>()>;

/// @brief Reader constructor
/// DataType must be either u8, u16, u24, u32, u40, u48, u56, or u64
reader(typename DataType::type value) :
reader(typename bitter_type::type value) :
m_value(value)
{
static_assert(size_in_bits<DataType>() == sum_sizes<Sizes...>(),
static_assert(size_in_bits<bitter_type>() == sum_sizes<Sizes...>(),
"size of the DataType is not equal to the sum of sizes");
}

Expand All @@ -52,14 +56,14 @@ class reader
/// @brief Function used as a wrapper, used for retrieving a field
/// based on the Index provide
template<uint32_t Index>
typename DataType::type get() const
typename bitter_type::type get() const
{
return field_get<DataType, BitNumbering, Index, Sizes...>(m_value);
return field_get<bitter_type, BitNumbering, Index, Sizes...>(m_value);
}

private:

/// Store the value containing the data used by the reader
typename DataType::type m_value;
typename bitter_type::type m_value;
};
}
48 changes: 41 additions & 7 deletions src/bitter/to_type.hpp
Expand Up @@ -5,31 +5,65 @@
#pragma once

#include <cstdint>
#include <type_traits>

#include "types.hpp"

namespace bitter
{
template<class NativeType>
struct to_type;
namespace detail
{
/// Base case for BitterTypes (see types.hpp)
template<class BitterType>
struct to_type
{
static_assert(BitterType::size > 0, "DataType must have size.");
static_assert(std::is_unsigned<typename BitterType::type>::value ,
"DataType must have a nested type which is unsigned. "
"See types.hpp");

using type = BitterType;
};

/// Special case for integer types
template<>
struct to_type<uint8_t>
{
using value = u8;
using type = u8;
};

template<>
struct to_type<uint16_t>
{
using value = u16;
using type = u16;
};

template<>
struct to_type<uint32_t>
{
using value = u32;
using type = u32;
};

template<>
struct to_type<uint64_t>
{
using value = u64;
using type = u64;
};
}
}

/// Helper function that converts a Type to a BitterType which is
/// defined in types.hpp
///
/// If the Type is a BitterType the function is just the identity
/// i.e.:
///
/// bitter::to_type<bitter::u8> == bitter::u8
///
/// However, for other integer types such as uint8_t we have:
///
/// bitter::to_type<uint8_t> == bitter::u8
///
template<class Type>
using to_type = typename detail::to_type<Type>::type;

}
18 changes: 11 additions & 7 deletions src/bitter/writer.hpp
Expand Up @@ -11,6 +11,7 @@
#include "field_size_in_bits.hpp"
#include "field_set.hpp"
#include "types.hpp"
#include "to_type.hpp"

#include <cstdint>
#include <cassert>
Expand All @@ -21,18 +22,21 @@ namespace bitter
/// the fields given in the variadic template Sizes
template
<
typename DataType,
typename Type,
typename BitNumbering,
uint32_t... Sizes
>
class writer
{
public:

// Get the bitter type
using bitter_type = to_type<Type>;

/// Constructor
writer()
{
static_assert(size_in_bits<DataType>() ==
static_assert(size_in_bits<bitter_type>() ==
sum_sizes<Sizes...>(),
"The size of the DataType in bits must exactly match the "
"sum of all the bit fields. If needed an unused bit "
Expand All @@ -42,25 +46,25 @@ class writer
/// @brief based on the provided index, the value is written
/// @param value is the data, wished to written to the field at Index
template<uint32_t Index>
void field(typename DataType::type value)
void field(typename bitter_type::type value)
{
static_assert(field_size_in_bits<Index, Sizes...>() <=
size_in_bits<DataType>(), "The field size in bits cannot "
size_in_bits<bitter_type>(), "The field size in bits cannot "
"be larger than the total size of the data type");

m_data = field_set<
DataType, BitNumbering, Index, Sizes...>(m_data, value);
bitter_type, BitNumbering, Index, Sizes...>(m_data, value);
}

/// @return The value create by the writer containing the bit fields
typename DataType::type data() const
typename bitter_type::type data() const
{
return m_data;
}

private:

/// The value built by the writer containing the different fields
typename DataType::type m_data = 0;
typename bitter_type::type m_data = 0;
};
}
40 changes: 40 additions & 0 deletions test/src/test_reader.cpp
Expand Up @@ -11,6 +11,46 @@

#include <gtest/gtest.h>

TEST(test_bit_reader, read_integer)
{
{
uint8_t input = 0x0FU;
auto reader = bitter::lsb0_reader<uint8_t, 4, 4>(input);

auto value = reader.field<0>().as<uint8_t>();
EXPECT_EQ(0xFU, value);
value = reader.field<1>().as<uint8_t>();
EXPECT_EQ(0x0U, value);
}
{
uint16_t input = 0x0FF0U;
auto reader = bitter::lsb0_reader<uint16_t, 8, 8>(input);

auto value = reader.field<0>().as<uint8_t>();
EXPECT_EQ(0xF0U, value);
value = reader.field<1>().as<uint8_t>();
EXPECT_EQ(0x0FU, value);
}
{
uint32_t input = 0x0FF0FF00U;
auto reader = bitter::lsb0_reader<uint32_t, 16, 16>(input);

auto value = reader.field<0>().as<uint16_t>();
EXPECT_EQ(0xFF00U, value);
value = reader.field<1>().as<uint16_t>();
EXPECT_EQ(0x0FF0U, value);
}
{
uint64_t input = 0xFFF0FF000FF0FF00U;
auto reader = bitter::lsb0_reader<uint64_t, 32, 32>(input);

auto value = reader.field<0>().as<uint32_t>();
EXPECT_EQ(0x0FF0FF00U, value);
value = reader.field<1>().as<uint32_t>();
EXPECT_EQ(0xFFF0FF00U, value);
}
}

TEST(test_bit_reader, read_bit)
{
uint32_t input = 0x0FF0FF00U;
Expand Down
13 changes: 13 additions & 0 deletions test/src/test_readme.cpp
Expand Up @@ -71,3 +71,16 @@ TEST(test_readme, reading_a_msb0_bit_field)
assert(value2 == 0x56);
assert(value3 == 0x78);
}

TEST(test_readme, reading_a_generic_sized_bit_field)
{
auto reader = bitter::msb0_reader<bitter::u24, 4, 12, 8>(0x123456U);

uint8_t value0 = reader.field<0>().as<uint8_t>(); // Read bits 0-3
uint8_t value1 = reader.field<1>().as<uint16_t>(); // Read bits 4-15
uint8_t value2 = reader.field<2>().as<uint8_t>(); // Read bits 16-23

assert(value0 == 0x1);
assert(value1 == 0x234);
assert(value2 == 0x56);
}
31 changes: 31 additions & 0 deletions test/src/test_to_type.cpp
@@ -0,0 +1,31 @@
// Copyright (c) Steinwurf ApS 2016.
// All Rights Reserved
//
// Distributed under the "BSD License". See the accompanying LICENSE.rst file.

#include <bitter/to_type.hpp>

#include <cstdint>

#include <gtest/gtest.h>

// Small Helper
template<class DataType, class BitterType>
using same = std::is_same<bitter::to_type<DataType>, BitterType>;

TEST(test_to_type, to_type)
{
EXPECT_TRUE((same<uint8_t, bitter::u8>::value));
EXPECT_TRUE((same<uint16_t, bitter::u16>::value));
EXPECT_TRUE((same<uint32_t, bitter::u32>::value));
EXPECT_TRUE((same<uint64_t, bitter::u64>::value));

EXPECT_TRUE((same<bitter::u8, bitter::u8>::value));
EXPECT_TRUE((same<bitter::u16, bitter::u16>::value));
EXPECT_TRUE((same<bitter::u24, bitter::u24>::value));
EXPECT_TRUE((same<bitter::u32, bitter::u32>::value));
EXPECT_TRUE((same<bitter::u40, bitter::u40>::value));
EXPECT_TRUE((same<bitter::u48, bitter::u48>::value));
EXPECT_TRUE((same<bitter::u56, bitter::u56>::value));
EXPECT_TRUE((same<bitter::u64, bitter::u64>::value));
}
32 changes: 32 additions & 0 deletions test/src/test_writer.cpp
Expand Up @@ -11,6 +11,38 @@
#include <iostream>
#include <gtest/gtest.h>

TEST(test_bit_writer, write_integer)
{
{
auto writer = bitter::lsb0_writer<uint8_t, 4, 4>();
writer.field<0>(0x0U);
writer.field<1>(0xFU);
auto value = writer.data();
EXPECT_EQ(value, 0xF0U);
}
{
auto writer = bitter::lsb0_writer<uint16_t, 8, 8>();
writer.field<0>(0xF0U);
writer.field<1>(0x0FU);
auto value = writer.data();
EXPECT_EQ(value, 0x0FF0U);
}
{
auto writer = bitter::lsb0_writer<uint32_t, 16, 16>();
writer.field<0>(0xF0F0U);
writer.field<1>(0x0F0FU);
auto value = writer.data();
EXPECT_EQ(value, 0x0F0FF0F0U);
}
{
auto writer = bitter::lsb0_writer<uint64_t, 32, 32>();
writer.field<0>(0xF0F0F0F0U);
writer.field<1>(0x0F0F0F0FU);
auto value = writer.data();
EXPECT_EQ(value, 0x0F0F0F0FF0F0F0F0U);
}
}

TEST(test_bit_writer, write_bit)
{
{
Expand Down

0 comments on commit 84afe84

Please sign in to comment.