Bit manipulation

An excellent example of D's ability to generate code at compile-time with mixins, is bit manipulation.

Simple bit manipulation

D offers the following operators for bit manipulation:

  • & bitwise and
  • | bitwise or
  • ~ bitwise negative
  • << bitwise signed left-shift
  • >> bitwise signed right-shift (preserves the sign of the high-order bit)
  • >>> bitwise unsigned right-shift

A practical example

A common example for bit manipulation is to read the value of a bit. D provides core.bitop.bt for most common tasks. However to get used to bit manipulation, let's start with some verbose code to test a bit:

enum posA = 1;
enum maskA = (1 << posA);
bool getFieldA()
{
    return _data & maskA;
}

A generalization is to test for blocks that are longer than 1. Hence a special read mask with the length of the block is needed and the data block is shifted accordingly before applying the mask:

enum posA = 1;
enum lenA = 3;
enum maskA = (1 << lenA) - 1; // ...0111
uint getFieldA()
{
    return (_data >> posA) & maskA;
}

Setting such a block can equivalently be defined by negating the mask and thus only allowing writes within the specified block:

void setFieldA(bool b);
{
    return (_data & ~maskAWrite) | ((b << aPos) & maskAWrite);
}

std.bitmanip to the rescue

It's a lot of fun to write custom bit manipulation code and D provides all the tools needed to do so. However, in most cases you don't want to copy and paste code like this, especially since it is very error-prone and hard to maintain. Hence in D std.bitmanip helps you to write maintainable, easy-to-read bit manipulations with std.bitmanip and the power of mixins - without sacrificing performance.

Have a look at the exercise section. A BitVector is defined, but it still uses just X bits and is nearly indistinguishable from a regular struct.

std.bitmanip and core.bitop contain more helpers that are very helpful for applications that require low-memory usage.

Padding and alignment

Since the compiler adds padding to variables whose size is less than the current OS memory layout (size_t.sizeof) e.g. bool, byte, char, it is recommended that you start with fields with high alignments.

In-depth

rdmd playground.d