std.bitmanip - D Programming Language

Menu

- Learn
- Documentation
  - Language Reference
  - Library Reference
  - Command-line Reference
  - Feature Overview
  - Articles
- Downloads
- Packages
- Community
  - Blog
  - Orgs using D
  - Twitter
  - Forums
  - IRC
  - Community Discord
  - Wiki
  - GitHub
  - Issues
  - Get involved
  - Contributors
  - Foundation
  - Security Team
  - Donate
  - Sponsors
- Resources
  - Tour
  - Books
  - Tutorials
  - Tools
  - Editors
  - IDEs
  - run.dlang.io
  - Visual D
  - Acknowledgments
  - D Style
  - Glossary
  - Sitemap

Search
Report a bug
If you spot a problem with this page, click here to create a Bugzilla issue.
Improve this page
Quickly fork, edit online, and submit a pull request for this page. Requires a signed-in GitHub account. This works well for small changes. If you'd like to make larger changes you may want to consider using a local clone.

# std.bitmanip

Bit-level manipulation facilities.

Category | Functions
--- | ---
Bit constructs | BitArray bitfields bitsSet
Endianness conversion | bigEndianToNative littleEndianToNative nativeToBigEndian nativeToLittleEndian swapEndian
Integral ranges | append peek read write
Floating-Point manipulation | DoubleRep FloatRep
Tagging | taggedClassRef taggedPointer

License:
Boost License 1.0.
Authors:
Walter Bright, Andrei Alexandrescu, Jonathan M Davis, Alex Rønne Petersen, Damian Ziemba, Amaury SECHET

Source std/bitmanip.d

string `bitfields`(T...)();
  Allows creating `bitfields` inside structs, classes and unions.
  A bitfield consists of one or more entries with a fixed number of bits reserved for each of the entries. The types of the entries can be bools, integral types or enumerated types, arbitrarily mixed. The most efficient type to store in `bitfields` is bool, followed by unsigned types, followed by signed types.
  Each non-bool entry of the bitfield will be represented by the number of bits specified by the user. The minimum and the maximum numbers that represent this domain can be queried by using the name of the variable followed by _min or _max.

  Limitation The number of bits in a bitfield is limited to 8, 16, 32 or 64. If padding is needed, an entry should be explicitly allocated with an empty name.

  Implementation details Bitfields are internally stored in an ubyte, ushort, uint or ulong depending on the number of bits used. The bits are filled in the order given by the parameters, starting with the lowest significant bit. The name of the (private) variable used for saving the bitfield is created by a prefix _bf and concatenating all of the variable names, each preceded by an underscore.

  Parameters:

  T | A list of template parameters divided into chunks of 3 items. Each chunk consists (in this order) of a type, a name and a number. Together they define an entry of the bitfield: a variable of the given type and name, which can hold as many bits as the number denotes.

  Returns:
  A string that can be used in a mixin to add the bitfield.
  See Also:
  std.typecons.BitFlags
  Examples:
  Create a bitfield pack of eight bits, which fit in one ubyte. The `bitfields` are allocated starting from the least significant bit, i.e. x occupies the two least significant bits of the `bitfields` storage.

      struct A
      {
          int a;
          mixin(bitfields!(
              uint, "x",    2,
              int,  "y",    3,
              uint, "z",    2,
              bool, "flag", 1));
      }

      A obj;
      obj.x = 2;
      obj.z = obj.x;

      writeln(obj.x); // 2
      writeln(obj.y); // 0
      writeln(obj.z); // 2
      writeln(obj.flag); // false

  Examples:
  The sum of all bit lengths in one bitfield instantiation must be exactly 8, 16, 32, or 64. If padding is needed, just allocate one bitfield with an empty name.

      struct A
      {
          mixin(bitfields!(
              bool, "flag1",    1,
              bool, "flag2",    1,
              uint, "",         6));
      }

      A a;
      writeln(a.flag1); // 0
      a.flag1 = 1;
      writeln(a.flag1); // 1
      a.flag1 = 0;
      writeln(a.flag1); // 0

  Examples:
  enums can be used too

      enum ABC { A, B, C }
      struct EnumTest
      {
          mixin(bitfields!(
                    ABC, "x", 2,
                    bool, "y", 1,
                    ubyte, "z", 5));
      }

enum auto `taggedPointer`(T : T*, string name, Ts...);
  This string mixin generator allows one to create tagged pointers inside structs and classes.
  A tagged pointer uses the bits known to be zero in a normal pointer or class reference to store extra information. For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero. One can store a 2-bit integer there.
  The example above creates a tagged pointer in the struct A. The pointer is of type uint* as specified by the first argument, and is named x, as specified by the second argument.
  Following arguments works the same way as bitfield's. The bitfield must fit into the bits known to be zero because of the pointer alignment.
  Examples:

      struct A
      {
          int a;
          mixin(taggedPointer!(
              uint*, "x",
              bool, "b1", 1,
              bool, "b2", 1));
      }
      A obj;
      obj.x = new uint;
      obj.b1 = true;
      obj.b2 = false;

template `taggedClassRef`(T, string name, Ts...) if (is(T == class))
  This string mixin generator allows one to create tagged class reference inside structs and classes.
  A tagged class reference uses the bits known to be zero in a normal class reference to store extra information. For example, a pointer to an integer must be 4-byte aligned, so there are 2 bits that are always known to be zero. One can store a 2-bit integer there.
  The example above creates a tagged reference to an Object in the struct A. This expects the same parameters as taggedPointer, except the first argument which must be a class type instead of a pointer type.
  Examples:

      struct A
      {
          int a;
          mixin(taggedClassRef!(
              Object, "o",
              uint, "i", 2));
      }
      A obj;
      obj.o = new Object();
      obj.i = 3;

alias `FloatRep` = FloatingPointRepresentation!float.FloatingPointRepresentation;
  Allows manipulating the fraction, exponent, and sign parts of a float separately. The definition is:

      struct FloatRep
      {
          union
          {
              float value;
              mixin(bitfields!(
                        uint,  "fraction", 23,
                        ubyte, "exponent",  8,
                        bool,  "sign",      1));
          }
          enum uint bias = 127, fractionBits = 23, exponentBits = 8, signBits = 1;
      }

  Examples:

      FloatRep rep = {value: 0};
      writeln(rep.fraction); // 0
      writeln(rep.exponent); // 0
      assert(!rep.sign);

      rep.value = 42;
      writeln(rep.fraction); // 2621440
      writeln(rep.exponent); // 132
      assert(!rep.sign);

      rep.value = 10;
      writeln(rep.fraction); // 2097152
      writeln(rep.exponent); // 130

  Examples:

      FloatRep rep = {value: 1};
      writeln(rep.fraction); // 0
      writeln(rep.exponent); // 127
      assert(!rep.sign);

      rep.exponent = 126;
      writeln(rep.value); // 0.5

      rep.exponent = 130;
      writeln(rep.value); // 8

  Examples:

      FloatRep rep = {value: 1};
      rep.value = -0.5;
      writeln(rep.fraction); // 0
      writeln(rep.exponent); // 126
      assert(rep.sign);

      rep.value = -1. / 3;
      writeln(rep.fraction); // 2796203
      writeln(rep.exponent); // 125
      assert(rep.sign);

alias `DoubleRep` = FloatingPointRepresentation!double.FloatingPointRepresentation;
  Allows manipulating the fraction, exponent, and sign parts of a double separately. The definition is:

      struct DoubleRep
      {
          union
          {
              double value;
              mixin(bitfields!(
                        ulong,   "fraction", 52,
                        ushort,  "exponent", 11,
                        bool,    "sign",      1));
          }
          enum uint bias = 1023, signBits = 1, fractionBits = 52, exponentBits = 11;
      }

  Examples:

      DoubleRep rep = {value: 0};
      writeln(rep.fraction); // 0
      writeln(rep.exponent); // 0
      assert(!rep.sign);

      rep.value = 42;
      writeln(rep.fraction); // 1407374883553280
      writeln(rep.exponent); // 1028
      assert(!rep.sign);

      rep.value = 10;
      writeln(rep.fraction); // 1125899906842624
      writeln(rep.exponent); // 1026

  Examples:

      DoubleRep rep = {value: 1};
      writeln(rep.fraction); // 0
      writeln(rep.exponent); // 1023
      assert(!rep.sign);

      rep.exponent = 1022;
      writeln(rep.value); // 0.5

      rep.exponent = 1026;
      writeln(rep.value); // 8

  Examples:

      DoubleRep rep = {value: 1};
      rep.value = -0.5;
      writeln(rep.fraction); // 0
      writeln(rep.exponent); // 1022
      assert(rep.sign);

      rep.value = -1. / 3;
      writeln(rep.fraction); // 1501199875790165
      writeln(rep.exponent); // 1021
      assert(rep.sign);

  Examples:
  Reading

      DoubleRep x;
      x.value = 1.0;
      assert(x.fraction == 0 && x.exponent == 1023 && !x.sign);
      x.value = -0.5;
      assert(x.fraction == 0 && x.exponent == 1022 && x.sign);
      x.value = 0.5;
      assert(x.fraction == 0 && x.exponent == 1022 && !x.sign);

  Examples:
  Writing

      DoubleRep x;
      x.fraction = 1125899906842624;
      x.exponent = 1025;
      x.sign = true;
      writeln(x.value); // -5.0

struct `BitArray`;
  A dynamic array of bits. Each bit in a `BitArray` can be manipulated individually or by the standard bitwise operators &, |, ^, ~, >>, << and also by other effective member functions; most of them work relative to the `BitArray`'s dimension (see dim), instead of its length.
  Examples:
  Slicing & bitsSet

      import std.algorithm.comparison : equal;
      import std.range : iota;

      bool[] buf = new bool[64 * 3];
      buf[0 .. 64] = true;
      BitArray b = BitArray(buf);
      assert(b.bitsSet.equal(iota(0, 64)));
      b <<= 64;
      assert(b.bitsSet.equal(iota(64, 128)));

  Examples:
  Concatenation and appending

      import std.algorithm.comparison : equal;

      auto b = BitArray([1, 0]);
      b ~= true;
      writeln(b[2]); // 1
      b ~= BitArray([0, 1]);
      auto c = BitArray([1, 0, 1, 0, 1]);
      writeln(b); // c
      assert(b.bitsSet.equal([0, 2, 4]));

  Examples:
  Bit flipping

      import std.algorithm.comparison : equal;

      auto b = BitArray([1, 1, 0, 1]);
      b &= BitArray([0, 1, 1, 0]);
      assert(b.bitsSet.equal([1]));
      b.flip;
      assert(b.bitsSet.equal([0, 2, 3]));

  Examples:
  String format of bitarrays

      import std.format : format;
      auto b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
      writeln(format("%b", b)); // "1_00001111_00001111"

  Examples:

      import std.format : format;

      BitArray b;

      b = BitArray([]);
      writeln(format("%s", b)); // "[]"
      assert(format("%b", b) is null);

      b = BitArray([1]);
      writeln(format("%s", b)); // "[1]"
      writeln(format("%b", b)); // "1"

      b = BitArray([0, 0, 0, 0]);
      writeln(format("%b", b)); // "0000"

      b = BitArray([0, 0, 0, 0, 1, 1, 1, 1]);
      writeln(format("%s", b)); // "[0, 0, 0, 0, 1, 1, 1, 1]"
      writeln(format("%b", b)); // "00001111"

      b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
      writeln(format("%s", b)); // "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]"
      writeln(format("%b", b)); // "00001111_00001111"

      b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1]);
      writeln(format("%b", b)); // "1_00001111"

      b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
      writeln(format("%b", b)); // "1_00001111_00001111"

  pure nothrow this(in bool[] `ba`);
    Creates a BitArray from a bool array, such that bool values read from left to right correspond to subsequent bits in the BitArray.
    Parameters:

    bool[] `ba` | Source array of bool values.

    Examples:

        import std.algorithm.comparison : equal;

        bool[] input = [true, false, false, true, true];
        auto a = BitArray(input);
        writeln(a.length); // 5
        assert(a.bitsSet.equal([0, 3, 4]));

        // This also works because an implicit cast to bool[] occurs for this array.
        auto b = BitArray([0, 0, 1]);
        writeln(b.length); // 3
        assert(b.bitsSet.equal([2]));

    Examples:

        import std.algorithm.comparison : equal;
        import std.array : array;
        import std.range : iota, repeat;

        BitArray a = true.repeat(70).array;
        writeln(a.length); // 70
        assert(a.bitsSet.equal(iota(0, 70)));

  pure nothrow @nogc this(void[] `v`, size_t `numbits`);
    Creates a BitArray from the raw contents of the source array. The source array is not copied but simply acts as the underlying array of bits, which stores data as size_t units.
    That means a particular care should be taken when passing an array of a type different than size_t, firstly because its length should be a multiple of size_t.sizeof, and secondly because how the bits are mapped:

        size_t[] source = [1, 2, 3, 3424234, 724398, 230947, 389492];
        enum sbits = size_t.sizeof * 8;
        auto ba = BitArray(source, source.length * sbits);
        foreach (n; 0 .. source.length * sbits)
        {
            auto nth_bit = cast(bool) (source[n / sbits] & (1L << (n % sbits)));
            assert(ba[n] == nth_bit);
        }

    The least significant bit in any size_t unit is the starting bit of this unit, and the most significant bit is the last bit of this unit. Therefore, passing e.g. an array of ints may result in a different BitArray depending on the processor's endianness.
    This constructor is the inverse of opCast.
    Parameters:

    void[] `v` | Source array. `v`.length must be a multple of size_t.sizeof.
    size_t `numbits` | Number of bits to be mapped from the source array, i.e. length of the created BitArray.

    Examples:

        import std.algorithm.comparison : equal;

        auto a = BitArray([1, 0, 0, 1, 1]);

        // Inverse of the cast.
        auto v = cast(void[]) a;
        auto b = BitArray(v, a.length);

        writeln(b.length); // 5
        assert(b.bitsSet.equal([0, 3, 4]));

        // a and b share the underlying data.
        a[0] = 0;
        writeln(b[0]); // 0
        writeln(a); // b

    Examples:

        import std.algorithm.comparison : equal;

        size_t[] source = [0b1100, 0b0011];
        enum sbits = size_t.sizeof * 8;
        auto ba = BitArray(source, source.length * sbits);
        // The least significant bit in each unit is this unit's starting bit.
        assert(ba.bitsSet.equal([2, 3, sbits, sbits + 1]));

    Examples:

        // Example from the doc for this constructor.
        static immutable size_t[] sourceData = [1, 0b101, 3, 3424234, 724398, 230947, 389492];
        size_t[] source = sourceData.dup;
        enum sbits = size_t.sizeof * 8;
        auto ba = BitArray(source, source.length * sbits);
        foreach (n; 0 .. source.length * sbits)
        {
            auto nth_bit = cast(bool) (source[n / sbits] & (1L << (n % sbits)));
            writeln(ba[n]); // nth_bit
        }

        // Example of mapping only part of the array.
        import std.algorithm.comparison : equal;

        auto bc = BitArray(source, sbits + 1);
        assert(bc.bitsSet.equal([0, sbits]));
        // Source array has not been modified.
        writeln(source); // sourceData

  const pure nothrow @nogc @property @safe size_t `dim`();
    Returns:
    Dimension i.e. the number of native words backing this BitArray.
    Technically, this is the length of the underlying array storing bits, which is equal to ceil(length / (size_t.sizeof * 8)), as bits are packed into size_t units.
  const pure nothrow @nogc @property @safe size_t `length`();
    Returns:
    Number of bits in the BitArray.
  pure nothrow @property @system size_t `length`(size_t `newlen`);
    Sets the amount of bits in the BitArray. Warning: increasing length may overwrite bits in the final word of the current underlying data regardless of whether it is shared between BitArray objects. i.e. D dynamic array extension semantics are not followed.
  const pure nothrow @nogc bool `opIndex`(size_t `i`);
    Gets the `i`'th bit in the BitArray.
    Examples:

        static void fun(const BitArray arr)
        {
            auto x = arr[0];
            writeln(x); // 1
        }
        BitArray a;
        a.length = 3;
        a[0] = 1;
        fun(a);

  pure nothrow @nogc bool `opIndexAssign`(bool `b`, size_t `i`);
    Sets the `i`'th bit in the BitArray.
  pure nothrow @nogc void `opSliceAssign`(bool `val`);
    Sets all the values in the BitArray to the value specified by `val`.
    Examples:

        import std.algorithm.comparison : equal;

        auto b = BitArray([1, 0, 1, 0, 1, 1]);

        b[] = true;
        // all bits are set
        assert(b.bitsSet.equal([0, 1, 2, 3, 4, 5]));

        b[] = false;
        // none of the bits are set
        assert(b.bitsSet.empty);

  pure nothrow @nogc void `opSliceAssign`(bool `val`, size_t `start`, size_t `end`);
    Sets the bits of a slice of BitArray starting at index `start` and ends at index ($D end - 1) with the values specified by `val`.
    Examples:

        import std.algorithm.comparison : equal;
        import std.range : iota;
        import std.stdio;

        auto b = BitArray([1, 0, 0, 0, 1, 1, 0]);
        b[1 .. 3] = true;
        assert(b.bitsSet.equal([0, 1, 2, 4, 5]));

        bool[72] bitArray;
        auto b1 = BitArray(bitArray);
        b1[63 .. 67] = true;
        assert(b1.bitsSet.equal([63, 64, 65, 66]));
        b1[63 .. 67] = false;
        assert(b1.bitsSet.empty);
        b1[0 .. 64] = true;
        assert(b1.bitsSet.equal(iota(0, 64)));
        b1[0 .. 64] = false;
        assert(b1.bitsSet.empty);

        bool[256] bitArray2;
        auto b2 = BitArray(bitArray2);
        b2[3 .. 245] = true;
        assert(b2.bitsSet.equal(iota(3, 245)));
        b2[3 .. 245] = false;
        assert(b2.bitsSet.empty);

  pure nothrow @nogc void `flip`();
    Flips all the bits in the BitArray
    Examples:

        import std.algorithm.comparison : equal;
        import std.range : iota;

        // positions 0, 2, 4 are set
        auto b = BitArray([1, 0, 1, 0, 1, 0]);
        b.flip();
        // after flipping, positions 1, 3, 5 are set
        assert(b.bitsSet.equal([1, 3, 5]));

        bool[270] bits;
        auto b1 = BitArray(bits);
        b1.flip();
        assert(b1.bitsSet.equal(iota(0, 270)));

  pure nothrow @nogc void `flip`(size_t `pos`);
    Flips a single bit, specified by `pos`
    Examples:

        auto ax = BitArray([1, 0, 0, 1]);
        ax.flip(0);
        writeln(ax[0]); // 0

        bool[200] y;
        y[90 .. 130] = true;
        auto ay = BitArray(y);
        ay.flip(100);
        writeln(ay[100]); // 0

  const pure nothrow @nogc scope @safe size_t `count`();
    Counts all the set bits in the BitArray
    Examples:

        auto a = BitArray([0, 1, 1, 0, 0, 1, 1]);
        writeln(a.count); // 4

        BitArray b;
        writeln(b.count); // 0

        bool[200] boolArray;
        boolArray[45 .. 130] = true;
        auto c = BitArray(boolArray);
        writeln(c.count); // 85

  const pure nothrow @property BitArray `dup`();
    Duplicates the BitArray and its contents.
    Examples:

        BitArray a;
        BitArray b;

        a.length = 3;
        a[0] = 1; a[1] = 0; a[2] = 1;
        b = a.dup;
        writeln(b.length); // 3
        foreach (i; 0 .. 3)
            writeln(b[i]); // (((i ^ 1) & 1) ? true : false)

  int `opApply`(scope int delegate(ref bool) `dg`);
  const int `opApply`(scope int delegate(bool) `dg`);
  int `opApply`(scope int delegate(size_t, ref bool) `dg`);
  const int `opApply`(scope int delegate(size_t, bool) `dg`);
    Support for foreach loops for BitArray.
    Examples:

        bool[] ba = [1,0,1];

        auto a = BitArray(ba);

        int i;
        foreach (b;a)
        {
            switch (i)
            {
                case 0: assert(b == true); break;
                case 1: assert(b == false); break;
                case 2: assert(b == true); break;
                default: assert(0);
            }
            i++;
        }

        foreach (j,b;a)
        {
            switch (j)
            {
                case 0: assert(b == true); break;
                case 1: assert(b == false); break;
                case 2: assert(b == true); break;
                default: assert(0);
            }
        }

  pure nothrow @nogc @property BitArray `reverse`() return;
    Reverses the bits of the BitArray.
    Examples:

        BitArray b;
        bool[5] data = [1,0,1,1,0];

        b = BitArray(data);
        b.reverse;
        foreach (i; 0 .. data.length)
            writeln(b[i]); // data[4 - i]

  pure nothrow @nogc @property BitArray `sort`() return;
    Sorts the BitArray's elements.
    Examples:

        size_t x = 0b1100011000;
        auto ba = BitArray(10, &x);
        ba.sort;
        foreach (i; 0 .. 6)
            writeln(ba[i]); // false
        foreach (i; 6 .. 10)
            writeln(ba[i]); // true

  const pure nothrow @nogc bool `opEquals`(const ref BitArray `a2`);
    Support for operators == and != for BitArray.
    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1];
        bool[] bc = [1,0,1,0,1,0,1];
        bool[] bd = [1,0,1,1,1];
        bool[] be = [1,0,1,0,1];
        bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0];
        bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];

        auto a = BitArray(ba);
        auto b = BitArray(bb);
        auto c = BitArray(bc);
        auto d = BitArray(bd);
        auto e = BitArray(be);
        auto f = BitArray(bf);
        auto g = BitArray(bg);

        assert(a != b);
        assert(a != c);
        assert(a != d);
        writeln(a); // e
        assert(f != g);

  const pure nothrow @nogc int `opCmp`(BitArray `a2`);
    Supports comparison operators for BitArray.
    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1];
        bool[] bc = [1,0,1,0,1,0,1];
        bool[] bd = [1,0,1,1,1];
        bool[] be = [1,0,1,0,1];
        bool[] bf = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];
        bool[] bg = [1,0,1,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);
        auto c = BitArray(bc);
        auto d = BitArray(bd);
        auto e = BitArray(be);
        auto f = BitArray(bf);
        auto g = BitArray(bg);

        assert(a >  b);
        assert(a >= b);
        assert(a <  c);
        assert(a <= c);
        assert(a <  d);
        assert(a <= d);
        writeln(a); // e
        assert(a <= e);
        assert(a >= e);
        assert(f <  g);
        assert(g <= g);

  const pure nothrow @nogc size_t `toHash`();
    Support for hashing for BitArray.
  inout pure nothrow @nogc inout(void)[] `opCast`(T : const(void[]))();
    Convert to void[].
  inout pure nothrow @nogc inout(size_t)[] `opCast`(T : const(size_t[]))();
    Convert to size_t[].
    Examples:

        import std.array : array;
        import std.range : repeat, take;

        // bit array with 300 elements
        auto a = BitArray(true.repeat.take(300).array);
        size_t[] v = cast(size_t[]) a;
        const blockSize = size_t.sizeof * 8;
        writeln(v.length); // (a.length + blockSize - 1) / blockSize

  const pure nothrow BitArray `opUnary`(string op)()
  if (op == "~");
    Support for unary operator ~ for BitArray.
    Examples:

        bool[] ba = [1,0,1,0,1];

        auto a = BitArray(ba);
        BitArray b = ~a;

        writeln(b[0]); // 0
        writeln(b[1]); // 1
        writeln(b[2]); // 0
        writeln(b[3]); // 1
        writeln(b[4]); // 0

  const pure nothrow BitArray `opBinary`(string op)(const BitArray `e2`)
  if (op == "-" || op == "&" || op == "|" || op == "^");
    Support for binary bitwise operators for BitArray.
    Examples:

        static bool[] ba = [1,0,1,0,1];
        static bool[] bb = [1,0,1,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);

        BitArray c = a & b;

        writeln(c[0]); // 1
        writeln(c[1]); // 0
        writeln(c[2]); // 1
        writeln(c[3]); // 0
        writeln(c[4]); // 0

    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);

        BitArray c = a | b;

        writeln(c[0]); // 1
        writeln(c[1]); // 0
        writeln(c[2]); // 1
        writeln(c[3]); // 1
        writeln(c[4]); // 1

    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);

        BitArray c = a ^ b;

        writeln(c[0]); // 0
        writeln(c[1]); // 0
        writeln(c[2]); // 0
        writeln(c[3]); // 1
        writeln(c[4]); // 1

    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);

        BitArray c = a - b;

        writeln(c[0]); // 0
        writeln(c[1]); // 0
        writeln(c[2]); // 0
        writeln(c[3]); // 0
        writeln(c[4]); // 1

  pure nothrow @nogc scope BitArray `opOpAssign`(string op)(const BitArray `e2`) return
  if (op == "-" || op == "&" || op == "|" || op == "^");
    Support for operator op= for BitArray.
    Examples:

        bool[] ba = [1,0,1,0,1,1,0,1,0,1];
        bool[] bb = [1,0,1,1,0];
        auto a = BitArray(ba);
        auto b = BitArray(bb);
        BitArray c = a;
        c.length = 5;
        c &= b;
        writeln(a[5]); // 1
        writeln(a[6]); // 0
        writeln(a[7]); // 1
        writeln(a[8]); // 0
        writeln(a[9]); // 1

    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);

        a &= b;
        writeln(a[0]); // 1
        writeln(a[1]); // 0
        writeln(a[2]); // 1
        writeln(a[3]); // 0
        writeln(a[4]); // 0

    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);

        a |= b;
        writeln(a[0]); // 1
        writeln(a[1]); // 0
        writeln(a[2]); // 1
        writeln(a[3]); // 1
        writeln(a[4]); // 1

    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);

        a ^= b;
        writeln(a[0]); // 0
        writeln(a[1]); // 0
        writeln(a[2]); // 0
        writeln(a[3]); // 1
        writeln(a[4]); // 1

    Examples:

        bool[] ba = [1,0,1,0,1];
        bool[] bb = [1,0,1,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);

        a -= b;
        writeln(a[0]); // 0
        writeln(a[1]); // 0
        writeln(a[2]); // 0
        writeln(a[3]); // 0
        writeln(a[4]); // 1

  pure nothrow scope BitArray `opOpAssign`(string op)(bool `b`) return
  if (op == "~");
  pure nothrow scope BitArray `opOpAssign`(string op)(BitArray `b`) return
  if (op == "~");
    Support for operator ~= for BitArray. Warning: This will overwrite a bit in the final word of the current underlying data regardless of whether it is shared between BitArray objects. i.e. D dynamic array concatenation semantics are not followed
    Examples:

        bool[] ba = [1,0,1,0,1];

        auto a = BitArray(ba);
        BitArray b;

        b = (a ~= true);
        writeln(a[0]); // 1
        writeln(a[1]); // 0
        writeln(a[2]); // 1
        writeln(a[3]); // 0
        writeln(a[4]); // 1
        writeln(a[5]); // 1

        writeln(b); // a

    Examples:

        bool[] ba = [1,0];
        bool[] bb = [0,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);
        BitArray c;

        c = (a ~= b);
        writeln(a.length); // 5
        writeln(a[0]); // 1
        writeln(a[1]); // 0
        writeln(a[2]); // 0
        writeln(a[3]); // 1
        writeln(a[4]); // 0

        writeln(c); // a

  const pure nothrow BitArray `opBinary`(string op)(bool `b`)
  if (op == "~");
  const pure nothrow BitArray `opBinaryRight`(string op)(bool `b`)
  if (op == "~");
  const pure nothrow BitArray `opBinary`(string op)(BitArray `b`)
  if (op == "~");
    Support for binary operator ~ for BitArray.
    Examples:

        bool[] ba = [1,0];
        bool[] bb = [0,1,0];

        auto a = BitArray(ba);
        auto b = BitArray(bb);
        BitArray c;

        c = (a ~ b);
        writeln(c.length); // 5
        writeln(c[0]); // 1
        writeln(c[1]); // 0
        writeln(c[2]); // 0
        writeln(c[3]); // 1
        writeln(c[4]); // 0

        c = (a ~ true);
        writeln(c.length); // 3
        writeln(c[0]); // 1
        writeln(c[1]); // 0
        writeln(c[2]); // 1

        c = (false ~ a);
        writeln(c.length); // 3
        writeln(c[0]); // 0
        writeln(c[1]); // 1
        writeln(c[2]); // 0

  pure nothrow @nogc void `opOpAssign`(string op)(size_t `nbits`)
  if (op == "<<");
    Operator <<= support.
    Shifts all the bits in the array to the left by the given number of bits. The leftmost bits are dropped, and 0's are appended to the end to fill up the vacant bits.
    Warning: unused bits in the final word up to the next word boundary may be overwritten by this operation. It does not attempt to preserve bits past the end of the array.
  pure nothrow @nogc void `opOpAssign`(string op)(size_t `nbits`)
  if (op == ">>");
    Operator >>= support.
    Shifts all the bits in the array to the right by the given number of bits. The rightmost bits are dropped, and 0's are inserted at the back to fill up the vacant bits.
    Warning: unused bits in the final word up to the next word boundary may be overwritten by this operation. It does not attempt to preserve bits past the end of the array.
    Examples:

        import std.format : format;

        auto b = BitArray([1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1]);

        b <<= 1;
        writeln(format("%b", b)); // "01100_10101101"

        b >>= 1;
        writeln(format("%b", b)); // "11001_01011010"

        b <<= 4;
        writeln(format("%b", b)); // "00001_10010101"

        b >>= 5;
        writeln(format("%b", b)); // "10010_10100000"

        b <<= 13;
        writeln(format("%b", b)); // "00000_00000000"

        b = BitArray([1, 0, 1, 1, 0, 1, 1, 1]);
        b >>= 8;
        writeln(format("%b", b)); // "00000000"

  const void `toString`(W)(ref W `sink`, ref scope const FormatSpec!char `fmt`)
  if (isOutputRange!(W, char));
    Return a string representation of this BitArray.
    Two format specifiers are supported:
    - %s which prints the bits as an array, and
    - %b which prints the bits as 8-bit byte packets
    separated with an underscore.
    Parameters:

    W `sink` | A char accepting output range.
    FormatSpec!char `fmt` | A std.format.FormatSpec which controls how the data is displayed.

    Examples:

        import std.format : format;

        auto b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);

        auto s1 = format("%s", b);
        writeln(s1); // "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]"

        auto s2 = format("%b", b);
        writeln(s2); // "00001111_00001111"

  const nothrow @property auto `bitsSet`();
    Return a lazy range of the indices of set bits.
    Examples:

        import std.algorithm.comparison : equal;

        auto b1 = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
        assert(b1.bitsSet.equal([4, 5, 6, 7, 12, 13, 14, 15]));

        BitArray b2;
        b2.length = 1000;
        b2[333] = true;
        b2[666] = true;
        b2[999] = true;
        assert(b2.bitsSet.equal([333, 666, 999]));

pure nothrow @nogc @safe T `swapEndian`(T)(const T `val`)
if (isIntegral!T || isSomeChar!T || isBoolean!T);
  Swaps the endianness of the given integral value or character.
  Examples:

      writeln(42.swapEndian); // 704643072
      assert(42.swapEndian.swapEndian == 42); // reflexive
      writeln(1.swapEndian); // 16777216

      writeln(true.swapEndian); // true
      writeln(byte(10).swapEndian); // 10
      writeln(char(10).swapEndian); // 10

      writeln(ushort(10).swapEndian); // 2560
      writeln(long(10).swapEndian); // 720575940379279360
      writeln(ulong(10).swapEndian); // 720575940379279360

pure nothrow @nogc @safe auto `nativeToBigEndian`(T)(const T `val`)
if (canSwapEndianness!T);
  Converts the given value from the native endianness to big endian and returns it as a ubyte[n] where n is the size of the given type.
  Returning a ubyte[n] helps prevent accidentally using a swapped value as a regular one (and in the case of floating point values, it's necessary, because the FPU will mess up any swapped floating point values. So, you can't actually have swapped floating point values as floating point values).
  real is not supported, because its size is implementation-dependent and therefore could vary from machine to machine (which could make it unusable if you tried to transfer it to another machine).
  Examples:

      int i = 12345;
      ubyte[4] swappedI = nativeToBigEndian(i);
      writeln(i); // bigEndianToNative!int(swappedI)

      float f = 123.45f;
      ubyte[4] swappedF = nativeToBigEndian(f);
      writeln(f); // bigEndianToNative!float(swappedF)

      const float cf = 123.45f;
      ubyte[4] swappedCF = nativeToBigEndian(cf);
      writeln(cf); // bigEndianToNative!float(swappedCF)

      double d = 123.45;
      ubyte[8] swappedD = nativeToBigEndian(d);
      writeln(d); // bigEndianToNative!double(swappedD)

      const double cd = 123.45;
      ubyte[8] swappedCD = nativeToBigEndian(cd);
      writeln(cd); // bigEndianToNative!double(swappedCD)

pure nothrow @nogc @safe T `bigEndianToNative`(T, size_t n)(ubyte[n] `val`)
if (canSwapEndianness!T && (n == T.sizeof));
  Converts the given value from big endian to the native endianness and returns it. The value is given as a ubyte[n] where n is the size of the target type. You must give the target type as a template argument, because there are multiple types with the same size and so the type of the argument is not enough to determine the return type.
  Taking a ubyte[n] helps prevent accidentally using a swapped value as a regular one (and in the case of floating point values, it's necessary, because the FPU will mess up any swapped floating point values. So, you can't actually have swapped floating point values as floating point values).
  Examples:

      ushort i = 12345;
      ubyte[2] swappedI = nativeToBigEndian(i);
      writeln(i); // bigEndianToNative!ushort(swappedI)

      dchar c = 'D';
      ubyte[4] swappedC = nativeToBigEndian(c);
      writeln(c); // bigEndianToNative!dchar(swappedC)

pure nothrow @nogc @safe auto `nativeToLittleEndian`(T)(const T `val`)
if (canSwapEndianness!T);
  Converts the given value from the native endianness to little endian and returns it as a ubyte[n] where n is the size of the given type.
  Returning a ubyte[n] helps prevent accidentally using a swapped value as a regular one (and in the case of floating point values, it's necessary, because the FPU will mess up any swapped floating point values. So, you can't actually have swapped floating point values as floating point values).
  Examples:

      int i = 12345;
      ubyte[4] swappedI = nativeToLittleEndian(i);
      writeln(i); // littleEndianToNative!int(swappedI)

      double d = 123.45;
      ubyte[8] swappedD = nativeToLittleEndian(d);
      writeln(d); // littleEndianToNative!double(swappedD)

pure nothrow @nogc @safe T `littleEndianToNative`(T, size_t n)(ubyte[n] `val`)
if (canSwapEndianness!T && (n == T.sizeof));
  Converts the given value from little endian to the native endianness and returns it. The value is given as a ubyte[n] where n is the size of the target type. You must give the target type as a template argument, because there are multiple types with the same size and so the type of the argument is not enough to determine the return type.
  Taking a ubyte[n] helps prevent accidentally using a swapped value as a regular one (and in the case of floating point values, it's necessary, because the FPU will mess up any swapped floating point values. So, you can't actually have swapped floating point values as floating point values).
  real is not supported, because its size is implementation-dependent and therefore could vary from machine to machine (which could make it unusable if you tried to transfer it to another machine).
  Examples:

      ushort i = 12345;
      ubyte[2] swappedI = nativeToLittleEndian(i);
      writeln(i); // littleEndianToNative!ushort(swappedI)

      dchar c = 'D';
      ubyte[4] swappedC = nativeToLittleEndian(c);
      writeln(c); // littleEndianToNative!dchar(swappedC)

T `peek`(T, Endian endianness = Endian.bigEndian, R)(R `range`)
if (canSwapEndianness!T && isForwardRange!R && is(ElementType!R : const(ubyte)));
T `peek`(T, Endian endianness = Endian.bigEndian, R)(R `range`, size_t `index`)
if (canSwapEndianness!T && isForwardRange!R && hasSlicing!R && is(ElementType!R : const(ubyte)));
T `peek`(T, Endian endianness = Endian.bigEndian, R)(R `range`, size_t* `index`)
if (canSwapEndianness!T && isForwardRange!R && hasSlicing!R && is(ElementType!R : const(ubyte)));
  Takes a range of ubytes and converts the first T.sizeof bytes to T. The value returned is converted from the given endianness to the native endianness. The range is not consumed.
  Parameters:

  T | The integral type to convert the first T.sizeof bytes to.
  endianness | The endianness that the bytes are assumed to be in.
  R `range` | The range to read from.
  size_t `index` | The index to start reading from (instead of starting at the front). If index is a pointer, then it is updated to the index after the bytes read. The overloads with index are only available if hasSlicing!R is true.

  Examples:

      ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
      writeln(buffer.peek!uint()); // 17110537
      writeln(buffer.peek!ushort()); // 261
      writeln(buffer.peek!ubyte()); // 1

      writeln(buffer.peek!uint(2)); // 369700095
      writeln(buffer.peek!ushort(2)); // 5641
      writeln(buffer.peek!ubyte(2)); // 22

      size_t index = 0;
      writeln(buffer.peek!ushort(&index)); // 261
      writeln(index); // 2

      writeln(buffer.peek!uint(&index)); // 369700095
      writeln(index); // 6

      writeln(buffer.peek!ubyte(&index)); // 8
      writeln(index); // 7

  Examples:

      import std.algorithm.iteration : filter;
      ubyte[] buffer = [1, 5, 22, 9, 44, 255, 7];
      auto range = filter!"true"(buffer);
      writeln(range.peek!uint()); // 17110537
      writeln(range.peek!ushort()); // 261
      writeln(range.peek!ubyte()); // 1

T `read`(T, Endian endianness = Endian.bigEndian, R)(ref R `range`)
if (canSwapEndianness!T && isInputRange!R && is(ElementType!R : const(ubyte)));
  Takes a range of ubytes and converts the first T.sizeof bytes to T. The value returned is converted from the given endianness to the native endianness. The T.sizeof bytes which are read are consumed from the range.
  Parameters:

  T | The integral type to convert the first T.sizeof bytes to.
  endianness | The endianness that the bytes are assumed to be in.
  R `range` | The range to read from.

  Examples:

      import std.range.primitives : empty;
      ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
      writeln(buffer.length); // 7

      writeln(buffer.read!ushort()); // 261
      writeln(buffer.length); // 5

      writeln(buffer.read!uint()); // 369700095
      writeln(buffer.length); // 1

      writeln(buffer.read!ubyte()); // 8
      assert(buffer.empty);

void `write`(T, Endian endianness = Endian.bigEndian, R)(R `range`, const T `value`, size_t `index`)
if (canSwapEndianness!T && isForwardRange!R && hasSlicing!R && is(ElementType!R : ubyte));
void `write`(T, Endian endianness = Endian.bigEndian, R)(R `range`, const T `value`, size_t* `index`)
if (canSwapEndianness!T && isForwardRange!R && hasSlicing!R && is(ElementType!R : ubyte));
  Takes an integral value, converts it to the given endianness, and writes it to the given range of ubytes as a sequence of T.sizeof ubytes starting at index. hasSlicing!R must be true.
  Parameters:

  T | The integral type to convert the first T.sizeof bytes to.
  endianness | The endianness to write the bytes in.
  R `range` | The range to write to.
  T `value` | The value to write.
  size_t `index` | The index to start writing to. If index is a pointer, then it is updated to the index after the bytes read.

  Examples:

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
      buffer.write!uint(29110231u, 0);
      writeln(buffer); // [1, 188, 47, 215, 0, 0, 0, 0]

      buffer.write!ushort(927, 0);
      writeln(buffer); // [3, 159, 47, 215, 0, 0, 0, 0]

      buffer.write!ubyte(42, 0);
      writeln(buffer); // [42, 159, 47, 215, 0, 0, 0, 0]

  Examples:

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0];
      buffer.write!uint(142700095u, 2);
      writeln(buffer); // [0, 0, 8, 129, 110, 63, 0, 0, 0]

      buffer.write!ushort(19839, 2);
      writeln(buffer); // [0, 0, 77, 127, 110, 63, 0, 0, 0]

      buffer.write!ubyte(132, 2);
      writeln(buffer); // [0, 0, 132, 127, 110, 63, 0, 0, 0]

  Examples:

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
      size_t index = 0;
      buffer.write!ushort(261, &index);
      writeln(buffer); // [1, 5, 0, 0, 0, 0, 0, 0]
      writeln(index); // 2

      buffer.write!uint(369700095u, &index);
      writeln(buffer); // [1, 5, 22, 9, 44, 255, 0, 0]
      writeln(index); // 6

      buffer.write!ubyte(8, &index);
      writeln(buffer); // [1, 5, 22, 9, 44, 255, 8, 0]
      writeln(index); // 7

  Examples:
  bool

      ubyte[] buffer = [0, 0];
      buffer.write!bool(false, 0);
      writeln(buffer); // [0, 0]

      buffer.write!bool(true, 0);
      writeln(buffer); // [1, 0]

      buffer.write!bool(true, 1);
      writeln(buffer); // [1, 1]

      buffer.write!bool(false, 1);
      writeln(buffer); // [1, 0]

      size_t index = 0;
      buffer.write!bool(false, &index);
      writeln(buffer); // [0, 0]
      writeln(index); // 1

      buffer.write!bool(true, &index);
      writeln(buffer); // [0, 1]
      writeln(index); // 2

  Examples:
  char(8-bit)

      ubyte[] buffer = [0, 0, 0];

      buffer.write!char('a', 0);
      writeln(buffer); // [97, 0, 0]

      buffer.write!char('b', 1);
      writeln(buffer); // [97, 98, 0]

      size_t index = 0;
      buffer.write!char('a', &index);
      writeln(buffer); // [97, 98, 0]
      writeln(index); // 1

      buffer.write!char('b', &index);
      writeln(buffer); // [97, 98, 0]
      writeln(index); // 2

      buffer.write!char('c', &index);
      writeln(buffer); // [97, 98, 99]
      writeln(index); // 3

  Examples:
  wchar (16bit - 2x ubyte)

      ubyte[] buffer = [0, 0, 0, 0];

      buffer.write!wchar('ą', 0);
      writeln(buffer); // [1, 5, 0, 0]

      buffer.write!wchar('”', 2);
      writeln(buffer); // [1, 5, 32, 29]

      size_t index = 0;
      buffer.write!wchar('ć', &index);
      writeln(buffer); // [1, 7, 32, 29]
      writeln(index); // 2

      buffer.write!wchar('ą', &index);
      writeln(buffer); // [1, 7, 1, 5]
      writeln(index); // 4

  Examples:
  dchar (32bit - 4x ubyte)

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];

      buffer.write!dchar('ą', 0);
      writeln(buffer); // [0, 0, 1, 5, 0, 0, 0, 0]

      buffer.write!dchar('”', 4);
      writeln(buffer); // [0, 0, 1, 5, 0, 0, 32, 29]

      size_t index = 0;
      buffer.write!dchar('ć', &index);
      writeln(buffer); // [0, 0, 1, 7, 0, 0, 32, 29]
      writeln(index); // 4

      buffer.write!dchar('ą', &index);
      writeln(buffer); // [0, 0, 1, 7, 0, 0, 1, 5]
      writeln(index); // 8

  Examples:
  float (32bit - 4x ubyte)

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];

      buffer.write!float(32.0f, 0);
      writeln(buffer); // [66, 0, 0, 0, 0, 0, 0, 0]

      buffer.write!float(25.0f, 4);
      writeln(buffer); // [66, 0, 0, 0, 65, 200, 0, 0]

      size_t index = 0;
      buffer.write!float(25.0f, &index);
      writeln(buffer); // [65, 200, 0, 0, 65, 200, 0, 0]
      writeln(index); // 4

      buffer.write!float(32.0f, &index);
      writeln(buffer); // [65, 200, 0, 0, 66, 0, 0, 0]
      writeln(index); // 8

  Examples:
  double (64bit - 8x ubyte)

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

      buffer.write!double(32.0, 0);
      writeln(buffer); // [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

      buffer.write!double(25.0, 8);
      writeln(buffer); // [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]

      size_t index = 0;
      buffer.write!double(25.0, &index);
      writeln(buffer); // [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]
      writeln(index); // 8

      buffer.write!double(32.0, &index);
      writeln(buffer); // [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]
      writeln(index); // 16

  Examples:
  enum

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

      enum Foo
      {
          one = 10,
          two = 20,
          three = 30
      }

      buffer.write!Foo(Foo.one, 0);
      writeln(buffer); // [0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0]

      buffer.write!Foo(Foo.two, 4);
      writeln(buffer); // [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 0]

      buffer.write!Foo(Foo.three, 8);
      writeln(buffer); // [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]

      size_t index = 0;
      buffer.write!Foo(Foo.three, &index);
      writeln(buffer); // [0, 0, 0, 30, 0, 0, 0, 20, 0, 0, 0, 30]
      writeln(index); // 4

      buffer.write!Foo(Foo.one, &index);
      writeln(buffer); // [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 30]
      writeln(index); // 8

      buffer.write!Foo(Foo.two, &index);
      writeln(buffer); // [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 20]
      writeln(index); // 12

  Examples:
  enum - float

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];

      enum Float: float
      {
          one = 32.0f,
          two = 25.0f
      }

      buffer.write!Float(Float.one, 0);
      writeln(buffer); // [66, 0, 0, 0, 0, 0, 0, 0]

      buffer.write!Float(Float.two, 4);
      writeln(buffer); // [66, 0, 0, 0, 65, 200, 0, 0]

      size_t index = 0;
      buffer.write!Float(Float.two, &index);
      writeln(buffer); // [65, 200, 0, 0, 65, 200, 0, 0]
      writeln(index); // 4

      buffer.write!Float(Float.one, &index);
      writeln(buffer); // [65, 200, 0, 0, 66, 0, 0, 0]
      writeln(index); // 8

  Examples:
  enum - double

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

      enum Double: double
      {
          one = 32.0,
          two = 25.0
      }

      buffer.write!Double(Double.one, 0);
      writeln(buffer); // [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]

      buffer.write!Double(Double.two, 8);
      writeln(buffer); // [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]

      size_t index = 0;
      buffer.write!Double(Double.two, &index);
      writeln(buffer); // [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]
      writeln(index); // 8

      buffer.write!Double(Double.one, &index);
      writeln(buffer); // [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]
      writeln(index); // 16

  Examples:
  enum - real

      ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0];

      enum Real: real
      {
          one = 32.0,
          two = 25.0
      }

      static assert(!__traits(compiles, buffer.write!Real(Real.one)));

void `append`(T, Endian endianness = Endian.bigEndian, R)(R `range`, const T `value`)
if (canSwapEndianness!T && isOutputRange!(R, ubyte));
  Takes an integral value, converts it to the given endianness, and appends it to the given range of ubytes (using put) as a sequence of T.sizeof ubytes starting at index. hasSlicing!R must be true.
  Parameters:

  T | The integral type to convert the first T.sizeof bytes to.
  endianness | The endianness to write the bytes in.
  R `range` | The range to append to.
  T `value` | The value to append.

  Examples:

      import std.array;
      auto buffer = appender!(const ubyte[])();
      buffer.append!ushort(261);
      writeln(buffer.data); // [1, 5]

      buffer.append!uint(369700095u);
      writeln(buffer.data); // [1, 5, 22, 9, 44, 255]

      buffer.append!ubyte(8);
      writeln(buffer.data); // [1, 5, 22, 9, 44, 255, 8]

  Examples:
  bool

      import std.array : appender;
      auto buffer = appender!(const ubyte[])();

      buffer.append!bool(true);
      writeln(buffer.data); // [1]

      buffer.append!bool(false);
      writeln(buffer.data); // [1, 0]

  Examples:
  char wchar dchar

      import std.array : appender;
      auto buffer = appender!(const ubyte[])();

      buffer.append!char('a');
      writeln(buffer.data); // [97]

      buffer.append!char('b');
      writeln(buffer.data); // [97, 98]

      buffer.append!wchar('ą');
      writeln(buffer.data); // [97, 98, 1, 5]

      buffer.append!dchar('ą');
          writeln(buffer.data); // [97, 98, 1, 5, 0, 0, 1, 5]

  Examples:
  float double

      import std.array : appender;
      auto buffer = appender!(const ubyte[])();

      buffer.append!float(32.0f);
      writeln(buffer.data); // [66, 0, 0, 0]

      buffer.append!double(32.0);
      writeln(buffer.data); // [66, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]

  Examples:
  enum

      import std.array : appender;
      auto buffer = appender!(const ubyte[])();

      enum Foo
      {
          one = 10,
          two = 20,
          three = 30
      }

      buffer.append!Foo(Foo.one);
      writeln(buffer.data); // [0, 0, 0, 10]

      buffer.append!Foo(Foo.two);
      writeln(buffer.data); // [0, 0, 0, 10, 0, 0, 0, 20]

      buffer.append!Foo(Foo.three);
      writeln(buffer.data); // [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]

  Examples:
  enum - bool

      import std.array : appender;
      auto buffer = appender!(const ubyte[])();

      enum Bool: bool
      {
          bfalse = false,
          btrue = true,
      }

      buffer.append!Bool(Bool.btrue);
      writeln(buffer.data); // [1]

      buffer.append!Bool(Bool.bfalse);
      writeln(buffer.data); // [1, 0]

      buffer.append!Bool(Bool.btrue);
      writeln(buffer.data); // [1, 0, 1]

  Examples:
  enum - float

      import std.array : appender;
      auto buffer = appender!(const ubyte[])();

      enum Float: float
      {
          one = 32.0f,
          two = 25.0f
      }

      buffer.append!Float(Float.one);
      writeln(buffer.data); // [66, 0, 0, 0]

      buffer.append!Float(Float.two);
      writeln(buffer.data); // [66, 0, 0, 0, 65, 200, 0, 0]

  Examples:
  enum - double

      import std.array : appender;
      auto buffer = appender!(const ubyte[])();

      enum Double: double
      {
          one = 32.0,
          two = 25.0
      }

      buffer.append!Double(Double.one);
      writeln(buffer.data); // [64, 64, 0, 0, 0, 0, 0, 0]

      buffer.append!Double(Double.two);
      writeln(buffer.data); // [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]

  Examples:
  enum - real

      import std.array : appender;
      auto buffer = appender!(const ubyte[])();

      enum Real: real
      {
          one = 32.0,
          two = 25.0
      }

      static assert(!__traits(compiles, buffer.append!Real(Real.one)));

pure nothrow @nogc auto `bitsSet`(T)(const T `value`)
if (isIntegral!T);
  Range that iterates the indices of the set bits in `value`. Index 0 corresponds to the least significant bit. For signed integers, the highest index corresponds to the sign bit.
  Examples:

      import std.algorithm.comparison : equal;
      import std.range : iota;

      assert(bitsSet(1).equal([0]));
      assert(bitsSet(5).equal([0, 2]));
      assert(bitsSet(-1).equal(iota(32)));
      assert(bitsSet(int.min).equal([31]));

Copyright © 1999-2023 by the D Language Foundation | Page generated by Ddoc on Wed Mar 1 22:14:26 2023