And this even more fancy. A class to handle a series of bit-fields
nearly a convenient like handling a series of dissimilar byte sequences
with a normal pointer. Conforming to C++ aliasing-constraints that
aliasing is only absolutely safe if you alias via memcpy().
#pragma once
#include <cstdint>
#include <cassert>
#include <iterator>
#include <cassert>
#include <type_traits>
#include <iterator>
#include <cstring>
#include <utility>
#include "bswap.h"
#include "range_check.h"
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable: 4554) // operator predence
#pragma warning(disable: 26495) // always initialize members
#endif
#if defined(__llvm__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wshift-op-parentheses"
#pragma clang diagnostic ignored "-Wlogical-op-parentheses"
#pragma clang diagnostic ignored "-Wbitwise-op-parentheses"
#endif
static_assert(std::bit_floor( sizeof(max_align_t) ) ==
sizeof(max_align_t), "sizeof(max_align_t) must be a power of two");
static_assert(sizeof(std::max_align_t) >= sizeof(std::uint64_t),
"sizeof(max_align_t) must be the same or a a larger power of two than
sizeof(uint64_t)");
template<typename RandIt>
concept bit_cursor_iterator = std::random_access_iterator<RandIt> &&
(sizeof(std::iter_value_t<RandIt>) == 1);
template<bit_cursor_iterator BitIt>
struct bit_cursor
{
using value_type = std::uint64_t;
using difference_type = std::ptrdiff_t;
bit_cursor() = delete;
constexpr bit_cursor( BitIt begin, std::ptrdiff_t offset = 0 ) noexcept;
constexpr bit_cursor( bit_cursor &other ) noexcept;
constexpr bit_cursor &operator =( bit_cursor const &other ) noexcept;
constexpr bool operator ==( bit_cursor const &other ) const noexcept;
constexpr bool operator !=( bit_cursor const &other ) const noexcept;
constexpr bit_cursor operator +( difference_type offset ) const noexcept;
constexpr bit_cursor operator -( difference_type offset ) const noexcept;
constexpr difference_type operator -( bit_cursor const &other ) const
noexcept;
constexpr bool operator <( bit_cursor const &other ) const noexcept;
constexpr bool operator <=( bit_cursor const &other ) const noexcept;
constexpr bool operator >( bit_cursor const &other ) const noexcept;
constexpr bool operator >=( bit_cursor const &other ) const noexcept;
constexpr bit_cursor &operator +=( difference_type offset ) noexcept;
constexpr bit_cursor &operator -=( difference_type offset ) noexcept;
constexpr std::uint64_t read_na( unsigned char bits ) noexcept;
constexpr void write_na( std::uint64_t value, unsigned char bits )
noexcept;
constexpr std::uint64_t read( unsigned char bits ) noexcept;
constexpr void write( std::uint64_t value, unsigned char bits ) noexcept;
template<unsigned char Bits>
requires (Bits > 0 && Bits <= 64)
constexpr std::uint64_t read_na() noexcept;
template<unsigned char Bits>
requires (Bits > 0 && Bits <= 64)
constexpr void write_na( std::uint64_t value ) noexcept;
template<unsigned char Bits>
requires (Bits > 0 && Bits <= 64)
constexpr std::uint64_t read() noexcept;
template<unsigned char Bits>
requires (Bits > 0 && Bits <= 64)
constexpr void write( std::uint64_t value ) noexcept;
constexpr void swap( bit_cursor &other ) noexcept;
private:
static std::uint64_t dRead( std::uint64_t const &data ) noexcept;
static void dWrite( std::uint64_t &data, std::uint64_t value ) noexcept;
constexpr bit_cursor( BitIt begin, std::uint64_t *data, unsigned char
shift ) noexcept;
static constexpr std::uint64_t bswap( std::uint64_t value ) noexcept;
#if !defined(NDEBUG)
BitIt m_safeData;
#endif
std::uint64_t *m_data;
unsigned char m_shift;
};
template<bit_cursor_iterator BitIt>
constexpr bit_cursor<BitIt>::bit_cursor( BitIt begin, std::ptrdiff_t
offset ) noexcept :
#if !defined(NDEBUG)
m_safeData( begin + (offset >> 3) ),
#endif
m_data( (std::uint64_t *)((std::size_t)std::to_address( begin ) +
(offset >> 3) & -(std::ptrdiff_t)sizeof(std::uint64_t)) ),
m_shift( (unsigned char)((char *)std::to_address( begin ) - (char
*)m_data) * 8 + (unsigned char)(offset & 7) )
{
}
template<bit_cursor_iterator BitIt>
constexpr bit_cursor<BitIt>::bit_cursor( bit_cursor &other ) noexcept :
#if !defined(NDEBUG)
m_safeData( other.m_safeData ),
#endif
m_data( other.m_data ),
m_shift( other.m_shift )
{
}
template<bit_cursor_iterator BitIt>
constexpr bit_cursor<BitIt> &bit_cursor<BitIt>::operator =( bit_cursor
const &other ) noexcept
{
#if !defined(NDEBUG)
m_safeData = other.m_safeData;
#endif
m_data = other.m_data;
m_shift = other.m_shift;
return *this;
}
template<bit_cursor_iterator BitIt>
constexpr bool bit_cursor<BitIt>::operator ==( bit_cursor const &other )
const noexcept
{
return m_data == other.m_data && m_shift == other.m_shift;
}
template<bit_cursor_iterator BitIt>
constexpr bool bit_cursor<BitIt>::operator !=( bit_cursor const &other )
const noexcept
{
return m_data != other.m_data || m_shift != other.m_shift;
}
template<bit_cursor_iterator BitIt>
constexpr bit_cursor<BitIt> bit_cursor<BitIt>::operator +(
difference_type offset ) const noexcept
{
using namespace std;
#if !defined(NDEBUG)
BitIt newSafeIt( m_safeData + ((ptrdiff_t)(m_shift & 7) + (offset >> 3)) );
#else
BitIt newSafeIt;
#endif
assert(in_range_add( (ptrdiff_t)m_shift, offset ));
ptrdiff_t iBit = (ptrdiff_t)m_shift + offset;
return bit_cursor( newSafeIt, m_data + (iBit >> 6), (unsigned char)iBit
% 64 );
}
template<bit_cursor_iterator BitIt>
constexpr bit_cursor<BitIt> bit_cursor<BitIt>::operator -(
difference_type offset ) const noexcept
{
using namespace std;
#if !defined(NDEBUG)
BitIt newSafeIt( m_safeData + ((ptrdiff_t)(m_shift & 7) - (offset >> 3)) );
#else
BitIt newSafeIt;
#endif
assert(in_range_sub( (ptrdiff_t)m_shift, offset ));
ptrdiff_t iBit = (ptrdiff_t)m_shift - offset;
return bit_cursor( newSafeIt, m_data - (iBit >> 6), (unsigned char)iBit
% 64 );
}
template<bit_cursor_iterator BitIt>
constexpr typename bit_cursor<BitIt>::difference_type
bit_cursor<BitIt>::operator -( bit_cursor const &other ) const noexcept
{
using namespace std;
signed char shiftDiff = (signed char)(m_shift - other.m_shift);
assert((m_data - other.m_data) * 64 / 64 == (m_data - other.m_data) &&
in_range_add( (m_data - other.m_data) * 64, (ptrdiff_t)shiftDiff ));
return (m_data - other.m_data) * 64 + (ptrdiff_t)shiftDiff;
}
template<bit_cursor_iterator BitIt>
constexpr bool bit_cursor<BitIt>::operator <( bit_cursor const &other )
const noexcept
{
return m_data < other.m_data || m_data == other.m_data && m_shift <
other.m_shift;
}
template<bit_cursor_iterator BitIt>
constexpr bool bit_cursor<BitIt>::operator <=( bit_cursor const &other )
const noexcept
{
return m_data < other.m_data || m_data == other.m_data && m_shift <=
other.m_shift;
}
template<bit_cursor_iterator BitIt>
constexpr bool bit_cursor<BitIt>::operator >( bit_cursor const &other )
const noexcept
{
return m_data > other.m_data || m_data == other.m_data && m_shift >
other.m_shift;
}
template<bit_cursor_iterator BitIt>
constexpr bool bit_cursor<BitIt>::operator >=( bit_cursor const &other )
const noexcept
{
return m_data > other.m_data || m_data == other.m_data && m_shift >=
other.m_shift;
}
template<bit_cursor_iterator BitIt>
constexpr bit_cursor<BitIt> &bit_cursor<BitIt>::operator +=(
difference_type offset ) noexcept
{
using namespace std;
#if !defined(NDEBUG)
(void)(m_safeData + ((ptrdiff_t)(m_shift & 7) + (offset >> 3)));
#endif
assert(in_range_add( (ptrdiff_t)m_shift, offset ));
ptrdiff_t iBit = (ptrdiff_t)m_shift + offset;
m_data += iBit >> 6;
m_shift = (unsigned char)iBit % 64;
return *this;
}
template<bit_cursor_iterator BitIt>
constexpr bit_cursor<BitIt> &bit_cursor<BitIt>::operator -=(
difference_type offset ) noexcept
{
using namespace std;
#if !defined(NDEBUG)
(void)(m_safeData + ((ptrdiff_t)(m_shift & 7) - (offset >> 3)));
#endif
assert(in_range_sub( (ptrdiff_t)m_shift, offset ));
ptrdiff_t iBit = (ptrdiff_t)m_shift - offset;
m_data -= iBit >> 6;
m_shift = (unsigned char)iBit % 64;
return *this;
}
template<bit_cursor_iterator BitIt>
constexpr std::uint64_t bit_cursor<BitIt>::read_na( unsigned char bits )
noexcept
{
using namespace std;
assert(bits <= 64);
#if !defined(NDEBUG)
(void)(m_safeData + ((ptrdiff_t)(m_shift & 7) + bits + 7) / 8);
#endif
unsigned char
shift = m_shift,
rShift = 64 - shift;
uint64_t
mask = bits != 64 ? ((uint64_t)1 << bits) - 1 : (uint64_t)-1,
value = bswap( dRead( m_data[0] ) ) >> shift & mask;
if( bits > rShift )
value |= bswap( dRead( m_data[1] ) ) << rShift & mask;
return value;
}
template<bit_cursor_iterator BitIt>
constexpr void bit_cursor<BitIt>::write_na( std::uint64_t value,
unsigned char bits ) noexcept
{
using namespace std;
assert(bits <= 64);
#if !defined(NDEBUG)
(void)(m_safeData + ((ptrdiff_t)(m_shift & 7) + bits + 7) / 8);
#endif
unsigned char
shift = m_shift,
rShift = 64 - shift;
uint64_t mask = bits != 64 ? ((uint64_t)1 << bits) - 1 : (uint64_t)-1;
assert(!(value & ~mask));
dWrite( m_data[0], bswap( bswap( dRead( m_data[0] ) ) & ~(mask <<
shift) | value << shift ) );
if( bits > rShift )
dWrite( m_data[1], bswap( bswap( dRead( m_data[1] ) ) & ~(mask >>
rShift) | value >> rShift ) );
}
template<bit_cursor_iterator BitIt>
constexpr std::uint64_t bit_cursor<BitIt>::read( unsigned char bits )
noexcept
{
std::uint64_t value = read_na( bits );
*this += (difference_type)bits;
return value;
}
template<bit_cursor_iterator BitIt>
constexpr void bit_cursor<BitIt>::write( std::uint64_t value, unsigned
char bits ) noexcept
{
write_na( value, bits );
*this += (difference_type)bits;
}
template<bit_cursor_iterator BitIt>
template<unsigned char Bits>
requires (Bits > 0 && Bits <= 64)
constexpr std::uint64_t bit_cursor<BitIt>::read_na() noexcept
{
using namespace std;
#if !defined(NDEBUG)
(void)(m_safeData + ((ptrdiff_t)(m_shift & 7) + Bits + 7) / 8);
#endif
unsigned char
shift = m_shift,
rShift = 64 - shift;
constexpr uint64_t MASK = Bits != 64 ? ((uint64_t)1 << Bits) - 1 :
(uint64_t)-1;
uint64_t value = bswap( dRead( m_data[0] ) ) >> shift & MASK;
if( Bits > rShift )
value |= bswap( dRead( m_data[1] ) ) << rShift & MASK;
return value;
}
template<bit_cursor_iterator BitIt>
template<unsigned char Bits>
requires (Bits > 0 && Bits <= 64)
constexpr void bit_cursor<BitIt>::write_na( std::uint64_t value ) noexcept
{
using namespace std;
#if !defined(NDEBUG)
(void)(m_safeData + ((ptrdiff_t)(m_shift & 7) + Bits + 7) / 8);
#endif
unsigned char
shift = m_shift,
rShift = 64 - shift;
constexpr uint64_t MASK = Bits != 64 ? ((uint64_t)1 << Bits) - 1 :
(uint64_t)-1;
assert(!(value & ~MASK));
dWrite( m_data[0], bswap( bswap( dRead( m_data[0] ) ) & ~(MASK <<
shift) | value << shift ) );
if( Bits > rShift )
dWrite( m_data[1], bswap( bswap( dRead( m_data[1] ) ) & ~(MASK >>
rShift) | value >> rShift ) );
}
template<bit_cursor_iterator BitIt>
template<unsigned char Bits>
requires (Bits > 0 && Bits <= 64)
constexpr std::uint64_t bit_cursor<BitIt>::read() noexcept
{
std::uint64_t value = this->template read_na<Bits>();
*this += (difference_type)Bits;
return value;
}
template<bit_cursor_iterator BitIt>
template<unsigned char Bits>
requires (Bits > 0 && Bits <= 64)
constexpr void bit_cursor<BitIt>::write( std::uint64_t value ) noexcept
{
this->template write_na<Bits>( value);
*this += (difference_type)Bits;
}
template<bit_cursor_iterator BitIt>
constexpr void bit_cursor<BitIt>::swap( bit_cursor &other ) noexcept
{
#if !defined(NDEBUG)
std::swap( m_safeData, other.m_safeData );
#endif
std::swap( m_data, other.m_data );
std::swap( m_shift, other.m_shift );
}
template<bit_cursor_iterator BitIt>
std::uint64_t bit_cursor<BitIt>::dRead( std::uint64_t const &data ) noexcept
{
uint64_t value;
std::memcpy( &value, &data, sizeof(std::uint64_t) );
return value;
}
template<bit_cursor_iterator BitIt>
void bit_cursor<BitIt>::dWrite( std::uint64_t &data, std::uint64_t value
) noexcept
{
std::memcpy( &data, &value, sizeof(std::uint64_t) );
}
template<bit_cursor_iterator BitIt>
constexpr bit_cursor<BitIt>::bit_cursor( BitIt begin, std::uint64_t
*data, unsigned char shift ) noexcept :
#if !defined(NDEBUG)
m_safeData( begin ),
#endif
m_data( data ),
m_shift( shift )
{
}
template<bit_cursor_iterator BitIt>
constexpr std::uint64_t bit_cursor<BitIt>::bswap( std::uint64_t value )
noexcept
{
return from_little_endian( value );
}
namespace std
{
template<typename BitIt>
constexpr void swap( bit_cursor<BitIt> &left, bit_cursor<BitIt> &right
) noexcept
{
left.swap( right );
}
}
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#if defined(__llvm__) || defined(__INTEL_LLVM_COMPILER)
#pragma clang diagnostic pop
#endif