aboutsummaryrefslogtreecommitdiff
path: root/libphobos/src/std/bitmanip.d
diff options
context:
space:
mode:
authorIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
committerIain Buclaw <ibuclaw@gcc.gnu.org>2018-10-28 19:51:47 +0000
commitb4c522fabd0df7be08882d2207df8b2765026110 (patch)
treeb5ffc312b0a441c1ba24323152aec463fdbe5e9f /libphobos/src/std/bitmanip.d
parent01ce9e31a02c8039d88e90f983735104417bf034 (diff)
downloadgcc-b4c522fabd0df7be08882d2207df8b2765026110.zip
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.gz
gcc-b4c522fabd0df7be08882d2207df8b2765026110.tar.bz2
Add D front-end, libphobos library, and D2 testsuite.
ChangeLog: * Makefile.def (target_modules): Add libphobos. (flags_to_pass): Add GDC, GDCFLAGS, GDC_FOR_TARGET and GDCFLAGS_FOR_TARGET. (dependencies): Make libphobos depend on libatomic, libbacktrace configure, and zlib configure. (language): Add language d. * Makefile.in: Rebuild. * Makefile.tpl (BUILD_EXPORTS): Add GDC and GDCFLAGS. (HOST_EXPORTS): Add GDC. (POSTSTAGE1_HOST_EXPORTS): Add GDC and GDC_FOR_BUILD. (BASE_TARGET_EXPORTS): Add GDC. (GDC_FOR_BUILD, GDC, GDCFLAGS): New variables. (GDC_FOR_TARGET, GDC_FLAGS_FOR_TARGET): New variables. (EXTRA_HOST_FLAGS): Add GDC. (STAGE1_FLAGS_TO_PASS): Add GDC. (EXTRA_TARGET_FLAGS): Add GDC and GDCFLAGS. * config-ml.in: Treat GDC and GDCFLAGS like other compiler/flag environment variables. * configure: Rebuild. * configure.ac: Add target-libphobos to target_libraries. Set and substitute GDC_FOR_BUILD and GDC_FOR_TARGET. config/ChangeLog: * multi.m4: Set GDC. gcc/ChangeLog: * Makefile.in (tm_d_file_list, tm_d_include_list): New variables. (TM_D_H, D_TARGET_DEF, D_TARGET_H, D_TARGET_OBJS): New variables. (tm_d.h, cs-tm_d.h, default-d.o): New rules. (d/d-target-hooks-def.h, s-d-target-hooks-def-h): New rules. (s-tm-texi): Also check timestamp on d-target.def. (generated_files): Add TM_D_H and d-target-hooks-def.h. (build/genhooks.o): Also depend on D_TARGET_DEF. * config.gcc (tm_d_file, d_target_objs, target_has_targetdm): New variables. * config/aarch64/aarch64-d.c: New file. * config/aarch64/aarch64-linux.h (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/aarch64/aarch64-protos.h (aarch64_d_target_versions): New prototype. * config/aarch64/aarch64.h (TARGET_D_CPU_VERSIONS): Define. * config/aarch64/t-aarch64 (aarch64-d.o): New rule. * config/arm/arm-d.c: New file. * config/arm/arm-protos.h (arm_d_target_versions): New prototype. * config/arm/arm.h (TARGET_D_CPU_VERSIONS): Define. * config/arm/linux-eabi.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/arm/t-arm (arm-d.o): New rule. * config/default-d.c: New file. * config/glibc-d.c: New file. * config/gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/i386/i386-d.c: New file. * config/i386/i386-protos.h (ix86_d_target_versions): New prototype. * config/i386/i386.h (TARGET_D_CPU_VERSIONS): Define. * config/i386/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. (GNU_USER_TARGET_D_CRITSEC_SIZE): Define. * config/i386/t-i386 (i386-d.o): New rule. * config/kfreebsd-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/kopensolaris-gnu.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/linux-android.h (ANDROID_TARGET_D_OS_VERSIONS): Define. * config/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/mips/linux-common.h (EXTRA_TARGET_D_OS_VERSIONS): Define. * config/mips/mips-d.c: New file. * config/mips/mips-protos.h (mips_d_target_versions): New prototype. * config/mips/mips.h (TARGET_D_CPU_VERSIONS): Define. * config/mips/t-mips (mips-d.o): New rule. * config/powerpcspe/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/powerpcspe/powerpcspe-d.c: New file. * config/powerpcspe/powerpcspe-protos.h (rs6000_d_target_versions): New prototype. * config/powerpcspe/powerpcspe.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/powerpcspe/powerpcspe.h (TARGET_D_CPU_VERSIONS): Define. * config/powerpcspe/t-powerpcspe (powerpcspe-d.o): New rule. * config/riscv/riscv-d.c: New file. * config/riscv/riscv-protos.h (riscv_d_target_versions): New prototype. * config/riscv/riscv.h (TARGET_D_CPU_VERSIONS): Define. * config/riscv/t-riscv (riscv-d.o): New rule. * config/rs6000/linux.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/linux64.h (GNU_USER_TARGET_D_OS_VERSIONS): Define. * config/rs6000/rs6000-d.c: New file. * config/rs6000/rs6000-protos.h (rs6000_d_target_versions): New prototype. * config/rs6000/rs6000.c (rs6000_output_function_epilogue): Support GNU D by using 0 as the language type. * config/rs6000/rs6000.h (TARGET_D_CPU_VERSIONS): Define. * config/rs6000/t-rs6000 (rs6000-d.o): New rule. * config/s390/s390-d.c: New file. * config/s390/s390-protos.h (s390_d_target_versions): New prototype. * config/s390/s390.h (TARGET_D_CPU_VERSIONS): Define. * config/s390/t-s390 (s390-d.o): New rule. * config/sparc/sparc-d.c: New file. * config/sparc/sparc-protos.h (sparc_d_target_versions): New prototype. * config/sparc/sparc.h (TARGET_D_CPU_VERSIONS): Define. * config/sparc/t-sparc (sparc-d.o): New rule. * config/t-glibc (glibc-d.o): New rule. * configure: Regenerated. * configure.ac (tm_d_file): New variable. (tm_d_file_list, tm_d_include_list, d_target_objs): Add substitutes. * doc/contrib.texi (Contributors): Add self for the D frontend. * doc/frontends.texi (G++ and GCC): Mention D as a supported language. * doc/install.texi (Configuration): Mention libphobos as an option for --enable-shared. Mention d as an option for --enable-languages. (Testing): Mention check-d as a target. * doc/invoke.texi (Overall Options): Mention .d, .dd, and .di as file name suffixes. Mention d as a -x option. * doc/sourcebuild.texi (Top Level): Mention libphobos. * doc/standards.texi (Standards): Add section on D language. * doc/tm.texi: Regenerated. * doc/tm.texi.in: Add @node for D language and ABI, and @hook for TARGET_CPU_VERSIONS, TARGET_D_OS_VERSIONS, and TARGET_D_CRITSEC_SIZE. * dwarf2out.c (is_dlang): New function. (gen_compile_unit_die): Use DW_LANG_D for D. (declare_in_namespace): Return module die for D, instead of adding extra declarations into the namespace. (gen_namespace_die): Generate DW_TAG_module for D. (gen_decl_die): Handle CONST_DECLSs for D. (dwarf2out_decl): Likewise. (prune_unused_types_walk_local_classes): Handle DW_tag_interface_type. (prune_unused_types_walk): Handle DW_tag_interface_type same as other kinds of aggregates. * gcc.c (default_compilers): Add entries for .d, .dd and .di. * genhooks.c: Include d/d-target.def. gcc/po/ChangeLog: * EXCLUDES: Add sources from d/dmd. gcc/testsuite/ChangeLog: * gcc.misc-tests/help.exp: Add D to option descriptions check. * gdc.dg/asan/asan.exp: New file. * gdc.dg/asan/gdc272.d: New test. * gdc.dg/compilable.d: New test. * gdc.dg/dg.exp: New file. * gdc.dg/gdc254.d: New test. * gdc.dg/gdc260.d: New test. * gdc.dg/gdc270a.d: New test. * gdc.dg/gdc270b.d: New test. * gdc.dg/gdc282.d: New test. * gdc.dg/gdc283.d: New test. * gdc.dg/imports/gdc170.d: New test. * gdc.dg/imports/gdc231.d: New test. * gdc.dg/imports/gdc239.d: New test. * gdc.dg/imports/gdc241a.d: New test. * gdc.dg/imports/gdc241b.d: New test. * gdc.dg/imports/gdc251a.d: New test. * gdc.dg/imports/gdc251b.d: New test. * gdc.dg/imports/gdc253.d: New test. * gdc.dg/imports/gdc254a.d: New test. * gdc.dg/imports/gdc256.d: New test. * gdc.dg/imports/gdc27.d: New test. * gdc.dg/imports/gdcpkg256/package.d: New test. * gdc.dg/imports/runnable.d: New test. * gdc.dg/link.d: New test. * gdc.dg/lto/lto.exp: New file. * gdc.dg/lto/ltotests_0.d: New test. * gdc.dg/lto/ltotests_1.d: New test. * gdc.dg/runnable.d: New test. * gdc.dg/simd.d: New test. * gdc.test/gdc-test.exp: New file. * lib/gdc-dg.exp: New file. * lib/gdc.exp: New file. libphobos/ChangeLog: * Makefile.am: New file. * Makefile.in: New file. * acinclude.m4: New file. * aclocal.m4: New file. * config.h.in: New file. * configure: New file. * configure.ac: New file. * d_rules.am: New file. * libdruntime/Makefile.am: New file. * libdruntime/Makefile.in: New file. * libdruntime/__entrypoint.di: New file. * libdruntime/__main.di: New file. * libdruntime/gcc/attribute.d: New file. * libdruntime/gcc/backtrace.d: New file. * libdruntime/gcc/builtins.d: New file. * libdruntime/gcc/config.d.in: New file. * libdruntime/gcc/deh.d: New file. * libdruntime/gcc/libbacktrace.d.in: New file. * libdruntime/gcc/unwind/arm.d: New file. * libdruntime/gcc/unwind/arm_common.d: New file. * libdruntime/gcc/unwind/c6x.d: New file. * libdruntime/gcc/unwind/generic.d: New file. * libdruntime/gcc/unwind/package.d: New file. * libdruntime/gcc/unwind/pe.d: New file. * m4/autoconf.m4: New file. * m4/druntime.m4: New file. * m4/druntime/cpu.m4: New file. * m4/druntime/libraries.m4: New file. * m4/druntime/os.m4: New file. * m4/gcc_support.m4: New file. * m4/gdc.m4: New file. * m4/libtool.m4: New file. * src/Makefile.am: New file. * src/Makefile.in: New file. * src/libgphobos.spec.in: New file. * testsuite/Makefile.am: New file. * testsuite/Makefile.in: New file. * testsuite/config/default.exp: New file. * testsuite/lib/libphobos-dg.exp: New file. * testsuite/lib/libphobos.exp: New file. * testsuite/testsuite_flags.in: New file. From-SVN: r265573
Diffstat (limited to 'libphobos/src/std/bitmanip.d')
-rw-r--r--libphobos/src/std/bitmanip.d4009
1 files changed, 4009 insertions, 0 deletions
diff --git a/libphobos/src/std/bitmanip.d b/libphobos/src/std/bitmanip.d
new file mode 100644
index 0000000..7802dff
--- /dev/null
+++ b/libphobos/src/std/bitmanip.d
@@ -0,0 +1,4009 @@
+// Written in the D programming language.
+
+/**
+Bit-level manipulation facilities.
+
+$(SCRIPT inhibitQuickIndex = 1;)
+$(BOOKTABLE,
+$(TR $(TH Category) $(TH Functions))
+$(TR $(TD Bit constructs) $(TD
+ $(LREF BitArray)
+ $(LREF bitfields)
+ $(LREF bitsSet)
+))
+$(TR $(TD Endianness conversion) $(TD
+ $(LREF bigEndianToNative)
+ $(LREF littleEndianToNative)
+ $(LREF nativeToBigEndian)
+ $(LREF nativeToLittleEndian)
+ $(LREF swapEndian)
+))
+$(TR $(TD Integral ranges) $(TD
+ $(LREF append)
+ $(LREF peek)
+ $(LREF read)
+ $(LREF write)
+))
+$(TR $(TD Floating-Point manipulation) $(TD
+ $(LREF DoubleRep)
+ $(LREF FloatRep)
+))
+$(TR $(TD Tagging) $(TD
+ $(LREF taggedClassRef)
+ $(LREF taggedPointer)
+))
+)
+
+Copyright: Copyright Digital Mars 2007 - 2011.
+License: $(HTTP www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
+Authors: $(HTTP digitalmars.com, Walter Bright),
+ $(HTTP erdani.org, Andrei Alexandrescu),
+ Jonathan M Davis,
+ Alex Rønne Petersen,
+ Damian Ziemba,
+ Amaury SECHET
+Source: $(PHOBOSSRC std/_bitmanip.d)
+*/
+/*
+ Copyright Digital Mars 2007 - 2012.
+Distributed under the Boost Software License, Version 1.0.
+ (See accompanying file LICENSE_1_0.txt or copy at
+ http://www.boost.org/LICENSE_1_0.txt)
+*/
+module std.bitmanip;
+
+//debug = bitarray; // uncomment to turn on debugging printf's
+
+import std.range.primitives;
+public import std.system : Endian;
+import std.traits;
+
+version (unittest)
+{
+ import std.stdio;
+}
+
+
+private string myToString(ulong n)
+{
+ import core.internal.string : UnsignedStringBuf, unsignedToTempString;
+ UnsignedStringBuf buf;
+ auto s = unsignedToTempString(n, buf);
+ return cast(string) s ~ (n > uint.max ? "UL" : "U");
+}
+
+private template createAccessors(
+ string store, T, string name, size_t len, size_t offset)
+{
+ static if (!name.length)
+ {
+ // No need to create any accessor
+ enum result = "";
+ }
+ else static if (len == 0)
+ {
+ // Fields of length 0 are always zero
+ enum result = "enum "~T.stringof~" "~name~" = 0;\n";
+ }
+ else
+ {
+ enum ulong
+ maskAllElse = ((~0uL) >> (64 - len)) << offset,
+ signBitCheck = 1uL << (len - 1);
+
+ static if (T.min < 0)
+ {
+ enum long minVal = -(1uL << (len - 1));
+ enum ulong maxVal = (1uL << (len - 1)) - 1;
+ alias UT = Unsigned!(T);
+ enum UT extendSign = cast(UT)~((~0uL) >> (64 - len));
+ }
+ else
+ {
+ enum ulong minVal = 0;
+ enum ulong maxVal = (~0uL) >> (64 - len);
+ enum extendSign = 0;
+ }
+
+ static if (is(T == bool))
+ {
+ static assert(len == 1);
+ enum result =
+ // getter
+ "@property bool " ~ name ~ "() @safe pure nothrow @nogc const { return "
+ ~"("~store~" & "~myToString(maskAllElse)~") != 0;}\n"
+ // setter
+ ~"@property void " ~ name ~ "(bool v) @safe pure nothrow @nogc { "
+ ~"if (v) "~store~" |= "~myToString(maskAllElse)~";"
+ ~"else "~store~" &= cast(typeof("~store~"))(-1-cast(typeof("~store~"))"~myToString(maskAllElse)~");}\n";
+ }
+ else
+ {
+ // getter
+ enum result = "@property "~T.stringof~" "~name~"() @safe pure nothrow @nogc const { auto result = "
+ ~"("~store~" & "
+ ~ myToString(maskAllElse) ~ ") >>"
+ ~ myToString(offset) ~ ";"
+ ~ (T.min < 0
+ ? "if (result >= " ~ myToString(signBitCheck)
+ ~ ") result |= " ~ myToString(extendSign) ~ ";"
+ : "")
+ ~ " return cast("~T.stringof~") result;}\n"
+ // setter
+ ~"@property void "~name~"("~T.stringof~" v) @safe pure nothrow @nogc { "
+ ~"assert(v >= "~name~`_min, "Value is smaller than the minimum value of bitfield '`~name~`'"); `
+ ~"assert(v <= "~name~`_max, "Value is greater than the maximum value of bitfield '`~name~`'"); `
+ ~store~" = cast(typeof("~store~"))"
+ ~" (("~store~" & (-1-cast(typeof("~store~"))"~myToString(maskAllElse)~"))"
+ ~" | ((cast(typeof("~store~")) v << "~myToString(offset)~")"
+ ~" & "~myToString(maskAllElse)~"));}\n"
+ // constants
+ ~"enum "~T.stringof~" "~name~"_min = cast("~T.stringof~")"
+ ~myToString(minVal)~"; "
+ ~" enum "~T.stringof~" "~name~"_max = cast("~T.stringof~")"
+ ~myToString(maxVal)~"; ";
+ }
+ }
+}
+
+private template createStoreName(Ts...)
+{
+ static if (Ts.length < 2)
+ enum createStoreName = "";
+ else
+ enum createStoreName = "_" ~ Ts[1] ~ createStoreName!(Ts[3 .. $]);
+}
+
+private template createStorageAndFields(Ts...)
+{
+ enum Name = createStoreName!Ts;
+ enum Size = sizeOfBitField!Ts;
+ static if (Size == ubyte.sizeof * 8)
+ alias StoreType = ubyte;
+ else static if (Size == ushort.sizeof * 8)
+ alias StoreType = ushort;
+ else static if (Size == uint.sizeof * 8)
+ alias StoreType = uint;
+ else static if (Size == ulong.sizeof * 8)
+ alias StoreType = ulong;
+ else
+ {
+ static assert(false, "Field widths must sum to 8, 16, 32, or 64");
+ alias StoreType = ulong; // just to avoid another error msg
+ }
+ enum result
+ = "private " ~ StoreType.stringof ~ " " ~ Name ~ ";"
+ ~ createFields!(Name, 0, Ts).result;
+}
+
+private template createFields(string store, size_t offset, Ts...)
+{
+ static if (Ts.length > 0)
+ enum result
+ = createAccessors!(store, Ts[0], Ts[1], Ts[2], offset).result
+ ~ createFields!(store, offset + Ts[2], Ts[3 .. $]).result;
+ else
+ enum result = "";
+}
+
+private ulong getBitsForAlign(ulong a)
+{
+ ulong bits = 0;
+ while ((a & 0x01) == 0)
+ {
+ bits++;
+ a >>= 1;
+ }
+
+ assert(a == 1, "alignment is not a power of 2");
+ return bits;
+}
+
+private template createReferenceAccessor(string store, T, ulong bits, string name)
+{
+ enum storage = "private void* " ~ store ~ "_ptr;\n";
+ enum storage_accessor = "@property ref size_t " ~ store ~ "() return @trusted pure nothrow @nogc const { "
+ ~ "return *cast(size_t*) &" ~ store ~ "_ptr;}\n"
+ ~ "@property void " ~ store ~ "(size_t v) @trusted pure nothrow @nogc { "
+ ~ "" ~ store ~ "_ptr = cast(void*) v;}\n";
+
+ enum mask = (1UL << bits) - 1;
+ // getter
+ enum ref_accessor = "@property "~T.stringof~" "~name~"() @trusted pure nothrow @nogc const { auto result = "
+ ~ "("~store~" & "~myToString(~mask)~"); "
+ ~ "return cast("~T.stringof~") cast(void*) result;}\n"
+ // setter
+ ~"@property void "~name~"("~T.stringof~" v) @trusted pure nothrow @nogc { "
+ ~"assert(((cast(typeof("~store~")) cast(void*) v) & "~myToString(mask)
+ ~`) == 0, "Value not properly aligned for '`~name~`'"); `
+ ~store~" = cast(typeof("~store~"))"
+ ~" (("~store~" & (cast(typeof("~store~")) "~myToString(mask)~"))"
+ ~" | ((cast(typeof("~store~")) cast(void*) v) & (cast(typeof("~store~")) "~myToString(~mask)~")));}\n";
+
+ enum result = storage ~ storage_accessor ~ ref_accessor;
+}
+
+private template sizeOfBitField(T...)
+{
+ static if (T.length < 2)
+ enum sizeOfBitField = 0;
+ else
+ enum sizeOfBitField = T[2] + sizeOfBitField!(T[3 .. $]);
+}
+
+private template createTaggedReference(T, ulong a, string name, Ts...)
+{
+ static assert(
+ sizeOfBitField!Ts <= getBitsForAlign(a),
+ "Fields must fit in the bits know to be zero because of alignment."
+ );
+ enum StoreName = createStoreName!(T, name, 0, Ts);
+ enum result
+ = createReferenceAccessor!(StoreName, T, sizeOfBitField!Ts, name).result
+ ~ createFields!(StoreName, 0, Ts, size_t, "", T.sizeof * 8 - sizeOfBitField!Ts).result;
+}
+
+/**
+Allows creating bit fields inside $(D_PARAM struct)s and $(D_PARAM
+class)es.
+
+Example:
+
+----
+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;
+----
+
+The example above creates a bitfield pack of eight bits, which fit in
+one $(D_PARAM ubyte). The bitfields are allocated starting from the
+least significant bit, i.e. x occupies the two least significant bits
+of the bitfields storage.
+
+The sum of all bit lengths in one $(D_PARAM bitfield) instantiation
+must be exactly 8, 16, 32, or 64. If padding is needed, just allocate
+one bitfield with an empty name.
+
+Example:
+
+----
+struct A
+{
+ mixin(bitfields!(
+ bool, "flag1", 1,
+ bool, "flag2", 1,
+ uint, "", 6));
+}
+----
+
+The type of a bit field can be any integral type or enumerated
+type. The most efficient type to store in bitfields is $(D_PARAM
+bool), followed by unsigned types, followed by signed types.
+*/
+
+template bitfields(T...)
+{
+ enum { bitfields = createStorageAndFields!T.result }
+}
+
+/**
+This string mixin generator allows one to create tagged pointers inside $(D_PARAM struct)s and $(D_PARAM class)es.
+
+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
+$(D uint*) as specified by the first argument, and is named x, as specified by the second
+argument.
+
+Following arguments works the same way as $(D bitfield)'s. The bitfield must fit into the
+bits known to be zero because of the pointer alignment.
+*/
+
+template taggedPointer(T : T*, string name, Ts...) {
+ enum taggedPointer = createTaggedReference!(T*, T.alignof, name, Ts).result;
+}
+
+///
+@safe unittest
+{
+ 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;
+}
+
+/**
+This string mixin generator allows one to create tagged class reference inside $(D_PARAM struct)s and $(D_PARAM class)es.
+
+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 $(D taggedPointer), except the first argument which must be a class type instead of a pointer type.
+*/
+
+template taggedClassRef(T, string name, Ts...)
+if (is(T == class))
+{
+ enum taggedClassRef = createTaggedReference!(T, 8, name, Ts).result;
+}
+
+///
+@safe unittest
+{
+ struct A
+ {
+ int a;
+ mixin(taggedClassRef!(
+ Object, "o",
+ uint, "i", 2));
+ }
+ A obj;
+ obj.o = new Object();
+ obj.i = 3;
+}
+
+@safe pure nothrow @nogc
+unittest
+{
+ // Degenerate bitfields (#8474 / #11160) tests mixed with range tests
+ struct Test1
+ {
+ mixin(bitfields!(uint, "a", 32,
+ uint, "b", 4,
+ uint, "c", 4,
+ uint, "d", 8,
+ uint, "e", 16,));
+
+ static assert(Test1.b_min == 0);
+ static assert(Test1.b_max == 15);
+ }
+
+ struct Test2
+ {
+ mixin(bitfields!(bool, "a", 0,
+ ulong, "b", 64));
+
+ static assert(Test2.b_min == ulong.min);
+ static assert(Test2.b_max == ulong.max);
+ }
+
+ struct Test1b
+ {
+ mixin(bitfields!(bool, "a", 0,
+ int, "b", 8));
+ }
+
+ struct Test2b
+ {
+ mixin(bitfields!(int, "a", 32,
+ int, "b", 4,
+ int, "c", 4,
+ int, "d", 8,
+ int, "e", 16,));
+
+ static assert(Test2b.b_min == -8);
+ static assert(Test2b.b_max == 7);
+ }
+
+ struct Test3b
+ {
+ mixin(bitfields!(bool, "a", 0,
+ long, "b", 64));
+
+ static assert(Test3b.b_min == long.min);
+ static assert(Test3b.b_max == long.max);
+ }
+
+ struct Test4b
+ {
+ mixin(bitfields!(long, "a", 32,
+ int, "b", 32));
+ }
+
+ // Sign extension tests
+ Test2b t2b;
+ Test4b t4b;
+ t2b.b = -5; assert(t2b.b == -5);
+ t2b.d = -5; assert(t2b.d == -5);
+ t2b.e = -5; assert(t2b.e == -5);
+ t4b.a = -5; assert(t4b.a == -5L);
+}
+
+@system unittest
+{
+ struct Test5
+ {
+ mixin(taggedPointer!(
+ int*, "a",
+ uint, "b", 2));
+ }
+
+ Test5 t5;
+ t5.a = null;
+ t5.b = 3;
+ assert(t5.a is null);
+ assert(t5.b == 3);
+
+ int myint = 42;
+ t5.a = &myint;
+ assert(t5.a is &myint);
+ assert(t5.b == 3);
+
+ struct Test6
+ {
+ mixin(taggedClassRef!(
+ Object, "o",
+ bool, "b", 1));
+ }
+
+ Test6 t6;
+ t6.o = null;
+ t6.b = false;
+ assert(t6.o is null);
+ assert(t6.b == false);
+
+ auto o = new Object();
+ t6.o = o;
+ t6.b = true;
+ assert(t6.o is o);
+ assert(t6.b == true);
+}
+
+@safe unittest
+{
+ static assert(!__traits(compiles,
+ taggedPointer!(
+ int*, "a",
+ uint, "b", 3)));
+
+ static assert(!__traits(compiles,
+ taggedClassRef!(
+ Object, "a",
+ uint, "b", 4)));
+
+ struct S {
+ mixin(taggedClassRef!(
+ Object, "a",
+ bool, "b", 1));
+ }
+
+ const S s;
+ void bar(S s) {}
+
+ static assert(!__traits(compiles, bar(s)));
+}
+
+@safe unittest
+{
+ // Bug #6686
+ union S {
+ ulong bits = ulong.max;
+ mixin (bitfields!(
+ ulong, "back", 31,
+ ulong, "front", 33)
+ );
+ }
+ S num;
+
+ num.bits = ulong.max;
+ num.back = 1;
+ assert(num.bits == 0xFFFF_FFFF_8000_0001uL);
+}
+
+@safe unittest
+{
+ // Bug #5942
+ struct S
+ {
+ mixin(bitfields!(
+ int, "a" , 32,
+ int, "b" , 32
+ ));
+ }
+
+ S data;
+ data.b = 42;
+ data.a = 1;
+ assert(data.b == 42);
+}
+
+@safe unittest
+{
+ struct Test
+ {
+ mixin(bitfields!(bool, "a", 1,
+ uint, "b", 3,
+ short, "c", 4));
+ }
+
+ @safe void test() pure nothrow
+ {
+ Test t;
+
+ t.a = true;
+ t.b = 5;
+ t.c = 2;
+
+ assert(t.a);
+ assert(t.b == 5);
+ assert(t.c == 2);
+ }
+
+ test();
+}
+
+@safe unittest
+{
+ {
+ static struct Integrals {
+ bool checkExpectations(bool eb, int ei, short es) { return b == eb && i == ei && s == es; }
+
+ mixin(bitfields!(
+ bool, "b", 1,
+ uint, "i", 3,
+ short, "s", 4));
+ }
+ Integrals i;
+ assert(i.checkExpectations(false, 0, 0));
+ i.b = true;
+ assert(i.checkExpectations(true, 0, 0));
+ i.i = 7;
+ assert(i.checkExpectations(true, 7, 0));
+ i.s = -8;
+ assert(i.checkExpectations(true, 7, -8));
+ i.s = 7;
+ assert(i.checkExpectations(true, 7, 7));
+ }
+
+ //Bug# 8876
+ {
+ struct MoreIntegrals {
+ bool checkExpectations(uint eu, ushort es, uint ei) { return u == eu && s == es && i == ei; }
+
+ mixin(bitfields!(
+ uint, "u", 24,
+ short, "s", 16,
+ int, "i", 24));
+ }
+
+ MoreIntegrals i;
+ assert(i.checkExpectations(0, 0, 0));
+ i.s = 20;
+ assert(i.checkExpectations(0, 20, 0));
+ i.i = 72;
+ assert(i.checkExpectations(0, 20, 72));
+ i.u = 8;
+ assert(i.checkExpectations(8, 20, 72));
+ i.s = 7;
+ assert(i.checkExpectations(8, 7, 72));
+ }
+
+ enum A { True, False }
+ enum B { One, Two, Three, Four }
+ static struct Enums {
+ bool checkExpectations(A ea, B eb) { return a == ea && b == eb; }
+
+ mixin(bitfields!(
+ A, "a", 1,
+ B, "b", 2,
+ uint, "", 5));
+ }
+ Enums e;
+ assert(e.checkExpectations(A.True, B.One));
+ e.a = A.False;
+ assert(e.checkExpectations(A.False, B.One));
+ e.b = B.Three;
+ assert(e.checkExpectations(A.False, B.Three));
+
+ static struct SingleMember {
+ bool checkExpectations(bool eb) { return b == eb; }
+
+ mixin(bitfields!(
+ bool, "b", 1,
+ uint, "", 7));
+ }
+ SingleMember f;
+ assert(f.checkExpectations(false));
+ f.b = true;
+ assert(f.checkExpectations(true));
+}
+
+// Issue 12477
+@system unittest
+{
+ import core.exception : AssertError;
+ import std.algorithm.searching : canFind;
+ import std.bitmanip : bitfields;
+
+ static struct S
+ {
+ mixin(bitfields!(
+ uint, "a", 6,
+ int, "b", 2));
+ }
+
+ S s;
+
+ try { s.a = uint.max; assert(0); }
+ catch (AssertError ae)
+ { assert(ae.msg.canFind("Value is greater than the maximum value of bitfield 'a'"), ae.msg); }
+
+ try { s.b = int.min; assert(0); }
+ catch (AssertError ae)
+ { assert(ae.msg.canFind("Value is smaller than the minimum value of bitfield 'b'"), ae.msg); }
+}
+
+/**
+ Allows manipulating the fraction, exponent, and sign parts of a
+ $(D_PARAM 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;
+}
+----
+*/
+
+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;
+}
+
+/**
+ Allows manipulating the fraction, exponent, and sign parts of a
+ $(D_PARAM 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;
+}
+----
+*/
+
+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;
+}
+
+@safe unittest
+{
+ // test 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);
+
+ // test writing
+ x.fraction = 1125899906842624;
+ x.exponent = 1025;
+ x.sign = true;
+ assert(x.value == -5.0);
+
+ // test enums
+ enum ABC { A, B, C }
+ struct EnumTest
+ {
+ mixin(bitfields!(
+ ABC, "x", 2,
+ bool, "y", 1,
+ ubyte, "z", 5));
+ }
+}
+
+@safe unittest
+{
+ // Issue #15305
+ struct S {
+ mixin(bitfields!(
+ bool, "alice", 1,
+ ulong, "bob", 63,
+ ));
+ }
+
+ S s;
+ s.bob = long.max - 1;
+ s.alice = false;
+ assert(s.bob == long.max - 1);
+}
+
+/**
+ * An array of bits.
+ */
+
+struct BitArray
+{
+private:
+
+ import core.bitop : bts, btr, bsf, bt;
+ import std.format : FormatSpec;
+
+ size_t _len;
+ size_t* _ptr;
+ enum bitsPerSizeT = size_t.sizeof * 8;
+
+ @property size_t fullWords() const @nogc pure nothrow
+ {
+ return _len / bitsPerSizeT;
+ }
+ // Number of bits after the last full word
+ @property size_t endBits() const @nogc pure nothrow
+ {
+ return _len % bitsPerSizeT;
+ }
+ // Bit mask to extract the bits after the last full word
+ @property size_t endMask() const @nogc pure nothrow
+ {
+ return (size_t(1) << endBits) - 1;
+ }
+ static size_t lenToDim(size_t len) @nogc pure nothrow @safe
+ {
+ return (len + (bitsPerSizeT-1)) / bitsPerSizeT;
+ }
+
+public:
+ /**********************************************
+ * Gets the amount of native words backing this $(D BitArray).
+ */
+ @property size_t dim() const @nogc pure nothrow @safe
+ {
+ return lenToDim(_len);
+ }
+
+ /**********************************************
+ * Gets the amount of bits in the $(D BitArray).
+ */
+ @property size_t length() const @nogc pure nothrow @safe
+ {
+ return _len;
+ }
+
+ /**********************************************
+ * Sets the amount of bits in the $(D BitArray).
+ * $(RED Warning: increasing length may overwrite bits in
+ * final word up to the next word boundary. i.e. D dynamic
+ * array extension semantics are not followed.)
+ */
+ @property size_t length(size_t newlen) pure nothrow @system
+ {
+ if (newlen != _len)
+ {
+ size_t olddim = dim;
+ immutable newdim = lenToDim(newlen);
+
+ if (newdim != olddim)
+ {
+ // Create a fake array so we can use D's realloc machinery
+ auto b = _ptr[0 .. olddim];
+ b.length = newdim; // realloc
+ _ptr = b.ptr;
+ }
+
+ _len = newlen;
+ }
+ return _len;
+ }
+
+ /**********************************************
+ * Gets the $(D i)'th bit in the $(D BitArray).
+ */
+ bool opIndex(size_t i) const @nogc pure nothrow
+ in
+ {
+ assert(i < _len);
+ }
+ body
+ {
+ return cast(bool) bt(_ptr, i);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opIndex.unittest\n");
+
+ void Fun(const BitArray arr)
+ {
+ auto x = arr[0];
+ assert(x == 1);
+ }
+ BitArray a;
+ a.length = 3;
+ a[0] = 1;
+ Fun(a);
+ }
+
+ /**********************************************
+ * Sets the $(D i)'th bit in the $(D BitArray).
+ */
+ bool opIndexAssign(bool b, size_t i) @nogc pure nothrow
+ in
+ {
+ assert(i < _len);
+ }
+ body
+ {
+ if (b)
+ bts(_ptr, i);
+ else
+ btr(_ptr, i);
+ return b;
+ }
+
+ /**********************************************
+ * Duplicates the $(D BitArray) and its contents.
+ */
+ @property BitArray dup() const pure nothrow
+ {
+ BitArray ba;
+
+ auto b = _ptr[0 .. dim].dup;
+ ba._len = _len;
+ ba._ptr = b.ptr;
+ return ba;
+ }
+
+ @system unittest
+ {
+ BitArray a;
+ BitArray b;
+ int i;
+
+ debug(bitarray) printf("BitArray.dup.unittest\n");
+
+ a.length = 3;
+ a[0] = 1; a[1] = 0; a[2] = 1;
+ b = a.dup;
+ assert(b.length == 3);
+ for (i = 0; i < 3; i++)
+ { debug(bitarray) printf("b[%d] = %d\n", i, b[i]);
+ assert(b[i] == (((i ^ 1) & 1) ? true : false));
+ }
+ }
+
+ /**********************************************
+ * Support for $(D foreach) loops for $(D BitArray).
+ */
+ int opApply(scope int delegate(ref bool) dg)
+ {
+ int result;
+
+ foreach (i; 0 .. _len)
+ {
+ bool b = opIndex(i);
+ result = dg(b);
+ this[i] = b;
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ /** ditto */
+ int opApply(scope int delegate(bool) dg) const
+ {
+ int result;
+
+ foreach (i; 0 .. _len)
+ {
+ immutable b = opIndex(i);
+ result = dg(b);
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ /** ditto */
+ int opApply(scope int delegate(size_t, ref bool) dg)
+ {
+ int result;
+
+ foreach (i; 0 .. _len)
+ {
+ bool b = opIndex(i);
+ result = dg(i, b);
+ this[i] = b;
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ /** ditto */
+ int opApply(scope int delegate(size_t, bool) dg) const
+ {
+ int result;
+
+ foreach (i; 0 .. _len)
+ {
+ immutable b = opIndex(i);
+ result = dg(i, b);
+ if (result)
+ break;
+ }
+ return result;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opApply unittest\n");
+
+ static 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);
+ }
+ }
+ }
+
+
+ /**********************************************
+ * Reverses the bits of the $(D BitArray).
+ */
+ @property BitArray reverse() @nogc pure nothrow
+ out (result)
+ {
+ assert(result == this);
+ }
+ body
+ {
+ if (_len >= 2)
+ {
+ bool t;
+ size_t lo, hi;
+
+ lo = 0;
+ hi = _len - 1;
+ for (; lo < hi; lo++, hi--)
+ {
+ t = this[lo];
+ this[lo] = this[hi];
+ this[hi] = t;
+ }
+ }
+ return this;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.reverse.unittest\n");
+
+ BitArray b;
+ static bool[5] data = [1,0,1,1,0];
+ int i;
+
+ b = BitArray(data);
+ b.reverse;
+ for (i = 0; i < data.length; i++)
+ {
+ assert(b[i] == data[4 - i]);
+ }
+ }
+
+
+ /**********************************************
+ * Sorts the $(D BitArray)'s elements.
+ */
+ @property BitArray sort() @nogc pure nothrow
+ out (result)
+ {
+ assert(result == this);
+ }
+ body
+ {
+ if (_len >= 2)
+ {
+ size_t lo, hi;
+
+ lo = 0;
+ hi = _len - 1;
+ while (1)
+ {
+ while (1)
+ {
+ if (lo >= hi)
+ goto Ldone;
+ if (this[lo] == true)
+ break;
+ lo++;
+ }
+
+ while (1)
+ {
+ if (lo >= hi)
+ goto Ldone;
+ if (this[hi] == false)
+ break;
+ hi--;
+ }
+
+ this[lo] = false;
+ this[hi] = true;
+
+ lo++;
+ hi--;
+ }
+ }
+ Ldone:
+ return this;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.sort.unittest\n");
+
+ __gshared size_t x = 0b1100011000;
+ __gshared ba = BitArray(10, &x);
+ ba.sort;
+ for (size_t i = 0; i < 6; i++)
+ assert(ba[i] == false);
+ for (size_t i = 6; i < 10; i++)
+ assert(ba[i] == true);
+ }
+
+
+ /***************************************
+ * Support for operators == and != for $(D BitArray).
+ */
+ bool opEquals(const ref BitArray a2) const @nogc pure nothrow
+ {
+ if (this.length != a2.length)
+ return false;
+ auto p1 = this._ptr;
+ auto p2 = a2._ptr;
+
+ if (p1[0 .. fullWords] != p2[0 .. fullWords])
+ return false;
+
+ if (!endBits)
+ return true;
+
+ auto i = fullWords;
+ return (p1[i] & endMask) == (p2[i] & endMask);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opEquals unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1];
+ static bool[] bc = [1,0,1,0,1,0,1];
+ static bool[] bd = [1,0,1,1,1];
+ static bool[] be = [1,0,1,0,1];
+ static 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];
+ static 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);
+ assert(a == e);
+ assert(f != g);
+ }
+
+ /***************************************
+ * Supports comparison operators for $(D BitArray).
+ */
+ int opCmp(BitArray a2) const @nogc pure nothrow
+ {
+ const lesser = this.length < a2.length ? &this : &a2;
+ immutable fullWords = lesser.fullWords;
+ immutable endBits = lesser.endBits;
+ auto p1 = this._ptr;
+ auto p2 = a2._ptr;
+
+ foreach (i; 0 .. fullWords)
+ {
+ if (p1[i] != p2[i])
+ {
+ return p1[i] & (size_t(1) << bsf(p1[i] ^ p2[i])) ? 1 : -1;
+ }
+ }
+
+ if (endBits)
+ {
+ immutable i = fullWords;
+ immutable diff = p1[i] ^ p2[i];
+ if (diff)
+ {
+ immutable index = bsf(diff);
+ if (index < endBits)
+ {
+ return p1[i] & (size_t(1) << index) ? 1 : -1;
+ }
+ }
+ }
+
+ // Standard:
+ // A bool value can be implicitly converted to any integral type,
+ // with false becoming 0 and true becoming 1
+ return (this.length > a2.length) - (this.length < a2.length);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCmp unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1];
+ static bool[] bc = [1,0,1,0,1,0,1];
+ static bool[] bd = [1,0,1,1,1];
+ static bool[] be = [1,0,1,0,1];
+ static 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];
+ static 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);
+ assert(a == e);
+ assert(a <= e);
+ assert(a >= e);
+ assert(f < g);
+ assert(g <= g);
+
+ bool[] v;
+ foreach (i; 1 .. 256)
+ {
+ v.length = i;
+ v[] = false;
+ auto x = BitArray(v);
+ v[i-1] = true;
+ auto y = BitArray(v);
+ assert(x < y);
+ assert(x <= y);
+ }
+
+ BitArray a1, a2;
+
+ for (size_t len = 4; len <= 256; len <<= 1)
+ {
+ a1.length = a2.length = len;
+ a1[len-2] = a2[len-1] = true;
+ assert(a1 > a2);
+ a1[len-2] = a2[len-1] = false;
+ }
+
+ foreach (j; 1 .. a1.length)
+ {
+ a1[j-1] = a2[j] = true;
+ assert(a1 > a2);
+ a1[j-1] = a2[j] = false;
+ }
+ }
+
+ /***************************************
+ * Support for hashing for $(D BitArray).
+ */
+ size_t toHash() const @nogc pure nothrow
+ {
+ size_t hash = 3557;
+ auto fullBytes = _len / 8;
+ foreach (i; 0 .. fullBytes)
+ {
+ hash *= 3559;
+ hash += (cast(byte*) this._ptr)[i];
+ }
+ foreach (i; 8*fullBytes .. _len)
+ {
+ hash *= 3571;
+ hash += this[i];
+ }
+ return hash;
+ }
+
+ /***************************************
+ * Set this $(D BitArray) to the contents of $(D ba).
+ */
+ this(bool[] ba) pure nothrow @system
+ {
+ length = ba.length;
+ foreach (i, b; ba)
+ {
+ this[i] = b;
+ }
+ }
+
+ // Deliberately undocumented: raw initialization of bit array.
+ this(size_t len, size_t* ptr)
+ {
+ _len = len;
+ _ptr = ptr;
+ }
+
+ /***************************************
+ * Map the $(D BitArray) onto $(D v), with $(D numbits) being the number of bits
+ * in the array. Does not copy the data. $(D v.length) must be a multiple of
+ * $(D size_t.sizeof). If there are unmapped bits in the final mapped word then
+ * these will be set to 0.
+ *
+ * This is the inverse of $(D opCast).
+ */
+ this(void[] v, size_t numbits) pure nothrow
+ in
+ {
+ assert(numbits <= v.length * 8);
+ assert(v.length % size_t.sizeof == 0);
+ }
+ body
+ {
+ _ptr = cast(size_t*) v.ptr;
+ _len = numbits;
+ if (endBits)
+ {
+ // Need to mask away extraneous bits from v.
+ _ptr[dim - 1] &= endMask;
+ }
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.init unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+
+ auto a = BitArray(ba);
+ void[] v;
+
+ v = cast(void[]) a;
+ auto b = BitArray(v, a.length);
+
+ assert(b[0] == 1);
+ assert(b[1] == 0);
+ assert(b[2] == 1);
+ assert(b[3] == 0);
+ assert(b[4] == 1);
+
+ a[0] = 0;
+ assert(b[0] == 0);
+
+ assert(a == b);
+ }
+
+ /***************************************
+ * Convert to $(D void[]).
+ */
+ void[] opCast(T : void[])() @nogc pure nothrow
+ {
+ return cast(void[])_ptr[0 .. dim];
+ }
+
+ /***************************************
+ * Convert to $(D size_t[]).
+ */
+ size_t[] opCast(T : size_t[])() @nogc pure nothrow
+ {
+ return _ptr[0 .. dim];
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCast unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+
+ auto a = BitArray(ba);
+ void[] v = cast(void[]) a;
+
+ assert(v.length == a.dim * size_t.sizeof);
+ }
+
+ /***************************************
+ * Support for unary operator ~ for $(D BitArray).
+ */
+ BitArray opCom() const pure nothrow
+ {
+ auto dim = this.dim;
+
+ BitArray result;
+ result.length = _len;
+
+ result._ptr[0 .. dim] = ~this._ptr[0 .. dim];
+
+ // Avoid putting garbage in extra bits
+ // Remove once we zero on length extension
+ if (endBits)
+ result._ptr[dim - 1] &= endMask;
+
+ return result;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCom unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+
+ auto a = BitArray(ba);
+ BitArray b = ~a;
+
+ assert(b[0] == 0);
+ assert(b[1] == 1);
+ assert(b[2] == 0);
+ assert(b[3] == 1);
+ assert(b[4] == 0);
+ }
+
+
+ /***************************************
+ * Support for binary bitwise operators for $(D BitArray).
+ */
+ BitArray opBinary(string op)(const BitArray e2) const pure nothrow
+ if (op == "-" || op == "&" || op == "|" || op == "^")
+ in
+ {
+ assert(_len == e2.length);
+ }
+ body
+ {
+ auto dim = this.dim;
+
+ BitArray result;
+ result.length = _len;
+
+ static if (op == "-")
+ result._ptr[0 .. dim] = this._ptr[0 .. dim] & ~e2._ptr[0 .. dim];
+ else
+ mixin("result._ptr[0 .. dim] = this._ptr[0 .. dim]"~op~" e2._ptr[0 .. dim];");
+
+ // Avoid putting garbage in extra bits
+ // Remove once we zero on length extension
+ if (endBits)
+ result._ptr[dim - 1] &= endMask;
+
+ return result;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opAnd unittest\n");
+
+ 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;
+
+ assert(c[0] == 1);
+ assert(c[1] == 0);
+ assert(c[2] == 1);
+ assert(c[3] == 0);
+ assert(c[4] == 0);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opOr unittest\n");
+
+ 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;
+
+ assert(c[0] == 1);
+ assert(c[1] == 0);
+ assert(c[2] == 1);
+ assert(c[3] == 1);
+ assert(c[4] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opXor unittest\n");
+
+ 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;
+
+ assert(c[0] == 0);
+ assert(c[1] == 0);
+ assert(c[2] == 0);
+ assert(c[3] == 1);
+ assert(c[4] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opSub unittest\n");
+
+ 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;
+
+ assert(c[0] == 0);
+ assert(c[1] == 0);
+ assert(c[2] == 0);
+ assert(c[3] == 0);
+ assert(c[4] == 1);
+ }
+
+
+ /***************************************
+ * Support for operator op= for $(D BitArray).
+ */
+ BitArray opOpAssign(string op)(const BitArray e2) @nogc pure nothrow
+ if (op == "-" || op == "&" || op == "|" || op == "^")
+ in
+ {
+ assert(_len == e2.length);
+ }
+ body
+ {
+ foreach (i; 0 .. fullWords)
+ {
+ static if (op == "-")
+ _ptr[i] &= ~e2._ptr[i];
+ else
+ mixin("_ptr[i] "~op~"= e2._ptr[i];");
+ }
+ if (!endBits)
+ return this;
+
+ size_t i = fullWords;
+ size_t endWord = _ptr[i];
+ static if (op == "-")
+ endWord &= ~e2._ptr[i];
+ else
+ mixin("endWord "~op~"= e2._ptr[i];");
+ _ptr[i] = (_ptr[i] & ~endMask) | (endWord & endMask);
+
+ return this;
+ }
+
+ @system unittest
+ {
+ static bool[] ba = [1,0,1,0,1,1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+ BitArray c = a;
+ c.length = 5;
+ c &= b;
+ assert(a[5] == 1);
+ assert(a[6] == 0);
+ assert(a[7] == 1);
+ assert(a[8] == 0);
+ assert(a[9] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opAndAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ a &= b;
+ assert(a[0] == 1);
+ assert(a[1] == 0);
+ assert(a[2] == 1);
+ assert(a[3] == 0);
+ assert(a[4] == 0);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opOrAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ a |= b;
+ assert(a[0] == 1);
+ assert(a[1] == 0);
+ assert(a[2] == 1);
+ assert(a[3] == 1);
+ assert(a[4] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opXorAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ a ^= b;
+ assert(a[0] == 0);
+ assert(a[1] == 0);
+ assert(a[2] == 0);
+ assert(a[3] == 1);
+ assert(a[4] == 1);
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opSubAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+ static bool[] bb = [1,0,1,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+
+ a -= b;
+ assert(a[0] == 0);
+ assert(a[1] == 0);
+ assert(a[2] == 0);
+ assert(a[3] == 0);
+ assert(a[4] == 1);
+ }
+
+ /***************************************
+ * Support for operator ~= for $(D BitArray).
+ * $(RED 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)
+ */
+
+ BitArray opCatAssign(bool b) pure nothrow
+ {
+ length = _len + 1;
+ this[_len - 1] = b;
+ return this;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCatAssign unittest\n");
+
+ static bool[] ba = [1,0,1,0,1];
+
+ auto a = BitArray(ba);
+ BitArray b;
+
+ b = (a ~= true);
+ assert(a[0] == 1);
+ assert(a[1] == 0);
+ assert(a[2] == 1);
+ assert(a[3] == 0);
+ assert(a[4] == 1);
+ assert(a[5] == 1);
+
+ assert(b == a);
+ }
+
+ /***************************************
+ * ditto
+ */
+
+ BitArray opCatAssign(BitArray b) pure nothrow
+ {
+ auto istart = _len;
+ length = _len + b.length;
+ for (auto i = istart; i < _len; i++)
+ this[i] = b[i - istart];
+ return this;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCatAssign unittest\n");
+
+ static bool[] ba = [1,0];
+ static bool[] bb = [0,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+ BitArray c;
+
+ c = (a ~= b);
+ assert(a.length == 5);
+ assert(a[0] == 1);
+ assert(a[1] == 0);
+ assert(a[2] == 0);
+ assert(a[3] == 1);
+ assert(a[4] == 0);
+
+ assert(c == a);
+ }
+
+ /***************************************
+ * Support for binary operator ~ for $(D BitArray).
+ */
+ BitArray opCat(bool b) const pure nothrow
+ {
+ BitArray r;
+
+ r = this.dup;
+ r.length = _len + 1;
+ r[_len] = b;
+ return r;
+ }
+
+ /** ditto */
+ BitArray opCat_r(bool b) const pure nothrow
+ {
+ BitArray r;
+
+ r.length = _len + 1;
+ r[0] = b;
+ foreach (i; 0 .. _len)
+ r[1 + i] = this[i];
+ return r;
+ }
+
+ /** ditto */
+ BitArray opCat(BitArray b) const pure nothrow
+ {
+ BitArray r;
+
+ r = this.dup;
+ r ~= b;
+ return r;
+ }
+
+ @system unittest
+ {
+ debug(bitarray) printf("BitArray.opCat unittest\n");
+
+ static bool[] ba = [1,0];
+ static bool[] bb = [0,1,0];
+
+ auto a = BitArray(ba);
+ auto b = BitArray(bb);
+ BitArray c;
+
+ c = (a ~ b);
+ assert(c.length == 5);
+ assert(c[0] == 1);
+ assert(c[1] == 0);
+ assert(c[2] == 0);
+ assert(c[3] == 1);
+ assert(c[4] == 0);
+
+ c = (a ~ true);
+ assert(c.length == 3);
+ assert(c[0] == 1);
+ assert(c[1] == 0);
+ assert(c[2] == 1);
+
+ c = (false ~ a);
+ assert(c.length == 3);
+ assert(c[0] == 0);
+ assert(c[1] == 1);
+ assert(c[2] == 0);
+ }
+
+ // Rolls double word (upper, lower) to the right by n bits and returns the
+ // lower word of the result.
+ private static size_t rollRight()(size_t upper, size_t lower, size_t nbits)
+ pure @safe nothrow @nogc
+ in
+ {
+ assert(nbits < bitsPerSizeT);
+ }
+ body
+ {
+ return (upper << (bitsPerSizeT - nbits)) | (lower >> nbits);
+ }
+
+ @safe unittest
+ {
+ static if (size_t.sizeof == 8)
+ {
+ size_t x = 0x12345678_90ABCDEF;
+ size_t y = 0xFEDBCA09_87654321;
+
+ assert(rollRight(x, y, 32) == 0x90ABCDEF_FEDBCA09);
+ assert(rollRight(y, x, 4) == 0x11234567_890ABCDE);
+ }
+ else static if (size_t.sizeof == 4)
+ {
+ size_t x = 0x12345678;
+ size_t y = 0x90ABCDEF;
+
+ assert(rollRight(x, y, 16) == 0x567890AB);
+ assert(rollRight(y, x, 4) == 0xF1234567);
+ }
+ else
+ static assert(0, "Unsupported size_t width");
+ }
+
+ // Rolls double word (upper, lower) to the left by n bits and returns the
+ // upper word of the result.
+ private static size_t rollLeft()(size_t upper, size_t lower, size_t nbits)
+ pure @safe nothrow @nogc
+ in
+ {
+ assert(nbits < bitsPerSizeT);
+ }
+ body
+ {
+ return (upper << nbits) | (lower >> (bitsPerSizeT - nbits));
+ }
+
+ @safe unittest
+ {
+ static if (size_t.sizeof == 8)
+ {
+ size_t x = 0x12345678_90ABCDEF;
+ size_t y = 0xFEDBCA09_87654321;
+
+ assert(rollLeft(x, y, 32) == 0x90ABCDEF_FEDBCA09);
+ assert(rollLeft(y, x, 4) == 0xEDBCA098_76543211);
+ }
+ else static if (size_t.sizeof == 4)
+ {
+ size_t x = 0x12345678;
+ size_t y = 0x90ABCDEF;
+
+ assert(rollLeft(x, y, 16) == 0x567890AB);
+ assert(rollLeft(y, x, 4) == 0x0ABCDEF1);
+ }
+ }
+
+ /**
+ * Operator $(D <<=) 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.
+ *
+ * $(RED 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.)
+ */
+ void opOpAssign(string op)(size_t nbits) @nogc pure nothrow
+ if (op == "<<")
+ {
+ size_t wordsToShift = nbits / bitsPerSizeT;
+ size_t bitsToShift = nbits % bitsPerSizeT;
+
+ if (wordsToShift < dim)
+ {
+ foreach_reverse (i; 1 .. dim - wordsToShift)
+ {
+ _ptr[i + wordsToShift] = rollLeft(_ptr[i], _ptr[i-1],
+ bitsToShift);
+ }
+ _ptr[wordsToShift] = rollLeft(_ptr[0], 0, bitsToShift);
+ }
+
+ import std.algorithm.comparison : min;
+ foreach (i; 0 .. min(wordsToShift, dim))
+ {
+ _ptr[i] = 0;
+ }
+ }
+
+ /**
+ * Operator $(D >>=) 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.
+ *
+ * $(RED 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.)
+ */
+ void opOpAssign(string op)(size_t nbits) @nogc pure nothrow
+ if (op == ">>")
+ {
+ size_t wordsToShift = nbits / bitsPerSizeT;
+ size_t bitsToShift = nbits % bitsPerSizeT;
+
+ if (wordsToShift + 1 < dim)
+ {
+ foreach (i; 0 .. dim - wordsToShift - 1)
+ {
+ _ptr[i] = rollRight(_ptr[i + wordsToShift + 1],
+ _ptr[i + wordsToShift], bitsToShift);
+ }
+ }
+
+ // The last word needs some care, as it must shift in 0's from past the
+ // end of the array.
+ if (wordsToShift < dim)
+ {
+ _ptr[dim - wordsToShift - 1] = rollRight(0, _ptr[dim - 1] & endMask,
+ bitsToShift);
+ }
+
+ import std.algorithm.comparison : min;
+ foreach (i; 0 .. min(wordsToShift, dim))
+ {
+ _ptr[dim - i - 1] = 0;
+ }
+ }
+
+ @system unittest
+ {
+ import std.format : format;
+
+ auto b = BitArray([1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1]);
+
+ b <<= 1;
+ assert(format("%b", b) == "01100_10101101");
+
+ b >>= 1;
+ assert(format("%b", b) == "11001_01011010");
+
+ b <<= 4;
+ assert(format("%b", b) == "00001_10010101");
+
+ b >>= 5;
+ assert(format("%b", b) == "10010_10100000");
+
+ b <<= 13;
+ assert(format("%b", b) == "00000_00000000");
+
+ b = BitArray([1, 0, 1, 1, 0, 1, 1, 1]);
+ b >>= 8;
+ assert(format("%b", b) == "00000000");
+
+ }
+
+ // Test multi-word case
+ @system unittest
+ {
+ import std.format : format;
+
+ // This has to be long enough to occupy more than one size_t. On 64-bit
+ // machines, this would be at least 64 bits.
+ auto b = BitArray([
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1,
+ ]);
+ b <<= 8;
+ assert(format("%b", b) ==
+ "00000000_10000000_"~
+ "11000000_11100000_"~
+ "11110000_11111000_"~
+ "11111100_11111110_"~
+ "11111111_10101010");
+
+ // Test right shift of more than one size_t's worth of bits
+ b <<= 68;
+ assert(format("%b", b) ==
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00001000");
+
+ b = BitArray([
+ 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1,
+ ]);
+ b >>= 8;
+ assert(format("%b", b) ==
+ "11000000_11100000_"~
+ "11110000_11111000_"~
+ "11111100_11111110_"~
+ "11111111_10101010_"~
+ "01010101_00000000");
+
+ // Test left shift of more than 1 size_t's worth of bits
+ b >>= 68;
+ assert(format("%b", b) ==
+ "01010000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000_"~
+ "00000000_00000000");
+ }
+
+ /***************************************
+ * Return a string representation of this BitArray.
+ *
+ * Two format specifiers are supported:
+ * $(LI $(B %s) which prints the bits as an array, and)
+ * $(LI $(B %b) which prints the bits as 8-bit byte packets)
+ * separated with an underscore.
+ */
+ void toString(scope void delegate(const(char)[]) sink,
+ FormatSpec!char fmt) const
+ {
+ switch (fmt.spec)
+ {
+ case 'b':
+ return formatBitString(sink);
+ case 's':
+ return formatBitArray(sink);
+ default:
+ throw new Exception("Unknown format specifier: %" ~ fmt.spec);
+ }
+ }
+
+ ///
+ @system unittest
+ {
+ import std.format : format;
+
+ debug(bitarray) printf("BitArray.toString unittest\n");
+ auto b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+
+ auto s1 = format("%s", b);
+ assert(s1 == "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]");
+
+ auto s2 = format("%b", b);
+ assert(s2 == "00001111_00001111");
+ }
+
+ /***************************************
+ * Return a lazy range of the indices of set bits.
+ */
+ @property auto bitsSet() const nothrow
+ {
+ import std.algorithm.iteration : filter, map, joiner;
+ import std.range : iota;
+
+ return iota(dim).
+ filter!(i => _ptr[i])().
+ map!(i => BitsSet!size_t(_ptr[i], i * bitsPerSizeT))().
+ joiner();
+ }
+
+ ///
+ @system unittest
+ {
+ 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]));
+ }
+
+ @system unittest
+ {
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ debug(bitarray) printf("BitArray.bitsSet unittest\n");
+ BitArray b;
+ enum wordBits = size_t.sizeof * 8;
+ b = BitArray([size_t.max], 0);
+ assert(b.bitsSet.empty);
+ b = BitArray([size_t.max], 1);
+ assert(b.bitsSet.equal([0]));
+ b = BitArray([size_t.max], wordBits);
+ assert(b.bitsSet.equal(iota(wordBits)));
+ b = BitArray([size_t.max, size_t.max], wordBits);
+ assert(b.bitsSet.equal(iota(wordBits)));
+ b = BitArray([size_t.max, size_t.max], wordBits + 1);
+ assert(b.bitsSet.equal(iota(wordBits + 1)));
+ b = BitArray([size_t.max, size_t.max], wordBits * 2);
+ assert(b.bitsSet.equal(iota(wordBits * 2)));
+ }
+
+ private void formatBitString(scope void delegate(const(char)[]) sink) const
+ {
+ if (!length)
+ return;
+
+ auto leftover = _len % 8;
+ foreach (idx; 0 .. leftover)
+ {
+ char[1] res = cast(char)(this[idx] + '0');
+ sink.put(res[]);
+ }
+
+ if (leftover && _len > 8)
+ sink.put("_");
+
+ size_t count;
+ foreach (idx; leftover .. _len)
+ {
+ char[1] res = cast(char)(this[idx] + '0');
+ sink.put(res[]);
+ if (++count == 8 && idx != _len - 1)
+ {
+ sink.put("_");
+ count = 0;
+ }
+ }
+ }
+
+ private void formatBitArray(scope void delegate(const(char)[]) sink) const
+ {
+ sink("[");
+ foreach (idx; 0 .. _len)
+ {
+ char[1] res = cast(char)(this[idx] + '0');
+ sink(res[]);
+ if (idx+1 < _len)
+ sink(", ");
+ }
+ sink("]");
+ }
+}
+
+@system unittest
+{
+ import std.format : format;
+
+ BitArray b;
+
+ b = BitArray([]);
+ assert(format("%s", b) == "[]");
+ assert(format("%b", b) is null);
+
+ b = BitArray([1]);
+ assert(format("%s", b) == "[1]");
+ assert(format("%b", b) == "1");
+
+ b = BitArray([0, 0, 0, 0]);
+ assert(format("%b", b) == "0000");
+
+ b = BitArray([0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%s", b) == "[0, 0, 0, 0, 1, 1, 1, 1]");
+ assert(format("%b", b) == "00001111");
+
+ b = BitArray([0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%s", b) == "[0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]");
+ assert(format("%b", b) == "00001111_00001111");
+
+ b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%b", b) == "1_00001111");
+
+ b = BitArray([1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]);
+ assert(format("%b", b) == "1_00001111_00001111");
+}
+
+/++
+ Swaps the endianness of the given integral value or character.
+ +/
+T swapEndian(T)(T val) @safe pure nothrow @nogc
+if (isIntegral!T || isSomeChar!T || isBoolean!T)
+{
+ static if (val.sizeof == 1)
+ return val;
+ else static if (isUnsigned!T)
+ return swapEndianImpl(val);
+ else static if (isIntegral!T)
+ return cast(T) swapEndianImpl(cast(Unsigned!T) val);
+ else static if (is(Unqual!T == wchar))
+ return cast(T) swapEndian(cast(ushort) val);
+ else static if (is(Unqual!T == dchar))
+ return cast(T) swapEndian(cast(uint) val);
+ else
+ static assert(0, T.stringof ~ " unsupported by swapEndian.");
+}
+
+private ushort swapEndianImpl(ushort val) @safe pure nothrow @nogc
+{
+ return ((val & 0xff00U) >> 8) |
+ ((val & 0x00ffU) << 8);
+}
+
+private uint swapEndianImpl(uint val) @trusted pure nothrow @nogc
+{
+ import core.bitop : bswap;
+ return bswap(val);
+}
+
+private ulong swapEndianImpl(ulong val) @trusted pure nothrow @nogc
+{
+ import core.bitop : bswap;
+ immutable ulong res = bswap(cast(uint) val);
+ return res << 32 | bswap(cast(uint)(val >> 32));
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong, char, wchar, dchar))
+ {
+ scope(failure) writefln("Failed type: %s", T.stringof);
+ T val;
+ const T cval;
+ immutable T ival;
+
+ assert(swapEndian(swapEndian(val)) == val);
+ assert(swapEndian(swapEndian(cval)) == cval);
+ assert(swapEndian(swapEndian(ival)) == ival);
+ assert(swapEndian(swapEndian(T.min)) == T.min);
+ assert(swapEndian(swapEndian(T.max)) == T.max);
+
+ foreach (i; 2 .. 10)
+ {
+ immutable T maxI = cast(T)(T.max / i);
+ immutable T minI = cast(T)(T.min / i);
+
+ assert(swapEndian(swapEndian(maxI)) == maxI);
+
+ static if (isSigned!T)
+ assert(swapEndian(swapEndian(minI)) == minI);
+ }
+
+ static if (isSigned!T)
+ assert(swapEndian(swapEndian(cast(T) 0)) == 0);
+
+ // used to trigger BUG6354
+ static if (T.sizeof > 1 && isUnsigned!T)
+ {
+ T left = 0xffU;
+ left <<= (T.sizeof - 1) * 8;
+ T right = 0xffU;
+
+ for (size_t i = 1; i < T.sizeof; ++i)
+ {
+ assert(swapEndian(left) == right);
+ assert(swapEndian(right) == left);
+ left >>= 8;
+ right <<= 8;
+ }
+ }
+ }
+}
+
+
+private union EndianSwapper(T)
+if (canSwapEndianness!T)
+{
+ Unqual!T value;
+ ubyte[T.sizeof] array;
+
+ static if (is(FloatingPointTypeOf!T == float))
+ uint intValue;
+ else static if (is(FloatingPointTypeOf!T == double))
+ ulong intValue;
+
+}
+
+
+/++
+ Converts the given value from the native endianness to big endian and
+ returns it as a $(D ubyte[n]) where $(D n) is the size of the given type.
+
+ Returning a $(D 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).
+
+ $(D 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).
+ +/
+auto nativeToBigEndian(T)(T val) @safe pure nothrow @nogc
+if (canSwapEndianness!T)
+{
+ return nativeToBigEndianImpl(val);
+}
+
+///
+@safe unittest
+{
+ int i = 12345;
+ ubyte[4] swappedI = nativeToBigEndian(i);
+ assert(i == bigEndianToNative!int(swappedI));
+
+ double d = 123.45;
+ ubyte[8] swappedD = nativeToBigEndian(d);
+ assert(d == bigEndianToNative!double(swappedD));
+}
+
+private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc
+if (isIntegral!T || isSomeChar!T || isBoolean!T)
+{
+ EndianSwapper!T es = void;
+
+ version (LittleEndian)
+ es.value = swapEndian(val);
+ else
+ es.value = val;
+
+ return es.array;
+}
+
+private auto nativeToBigEndianImpl(T)(T val) @safe pure nothrow @nogc
+if (isFloatOrDouble!T)
+{
+ version (LittleEndian)
+ return floatEndianImpl!(T, true)(val);
+ else
+ return floatEndianImpl!(T, false)(val);
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ char, wchar, dchar
+ /* The trouble here is with floats and doubles being compared against nan
+ * using a bit compare. There are two kinds of nans, quiet and signaling.
+ * When a nan passes through the x87, it converts signaling to quiet.
+ * When a nan passes through the XMM, it does not convert signaling to quiet.
+ * float.init is a signaling nan.
+ * The binary API sometimes passes the data through the XMM, sometimes through
+ * the x87, meaning these will fail the 'is' bit compare under some circumstances.
+ * I cannot think of a fix for this that makes consistent sense.
+ */
+ /*,float, double*/))
+ {
+ scope(failure) writefln("Failed type: %s", T.stringof);
+ T val;
+ const T cval;
+ immutable T ival;
+
+ //is instead of == because of NaN for floating point values.
+ assert(bigEndianToNative!T(nativeToBigEndian(val)) is val);
+ assert(bigEndianToNative!T(nativeToBigEndian(cval)) is cval);
+ assert(bigEndianToNative!T(nativeToBigEndian(ival)) is ival);
+ assert(bigEndianToNative!T(nativeToBigEndian(T.min)) == T.min);
+ assert(bigEndianToNative!T(nativeToBigEndian(T.max)) == T.max);
+
+ static if (isSigned!T)
+ assert(bigEndianToNative!T(nativeToBigEndian(cast(T) 0)) == 0);
+
+ static if (!is(T == bool))
+ {
+ foreach (i; [2, 4, 6, 7, 9, 11])
+ {
+ immutable T maxI = cast(T)(T.max / i);
+ immutable T minI = cast(T)(T.min / i);
+
+ assert(bigEndianToNative!T(nativeToBigEndian(maxI)) == maxI);
+
+ static if (T.sizeof > 1)
+ assert(nativeToBigEndian(maxI) != nativeToLittleEndian(maxI));
+ else
+ assert(nativeToBigEndian(maxI) == nativeToLittleEndian(maxI));
+
+ static if (isSigned!T)
+ {
+ assert(bigEndianToNative!T(nativeToBigEndian(minI)) == minI);
+
+ static if (T.sizeof > 1)
+ assert(nativeToBigEndian(minI) != nativeToLittleEndian(minI));
+ else
+ assert(nativeToBigEndian(minI) == nativeToLittleEndian(minI));
+ }
+ }
+ }
+
+ static if (isUnsigned!T || T.sizeof == 1 || is(T == wchar))
+ assert(nativeToBigEndian(T.max) == nativeToLittleEndian(T.max));
+ else
+ assert(nativeToBigEndian(T.max) != nativeToLittleEndian(T.max));
+
+ static if (isUnsigned!T || T.sizeof == 1 || isSomeChar!T)
+ assert(nativeToBigEndian(T.min) == nativeToLittleEndian(T.min));
+ else
+ assert(nativeToBigEndian(T.min) != nativeToLittleEndian(T.min));
+ }
+}
+
+
+/++
+ Converts the given value from big endian to the native endianness and
+ returns it. The value is given as a $(D ubyte[n]) where $(D 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 $(D 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).
+ +/
+T bigEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if (canSwapEndianness!T && n == T.sizeof)
+{
+ return bigEndianToNativeImpl!(T, n)(val);
+}
+
+///
+@safe unittest
+{
+ ushort i = 12345;
+ ubyte[2] swappedI = nativeToBigEndian(i);
+ assert(i == bigEndianToNative!ushort(swappedI));
+
+ dchar c = 'D';
+ ubyte[4] swappedC = nativeToBigEndian(c);
+ assert(c == bigEndianToNative!dchar(swappedC));
+}
+
+private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if ((isIntegral!T || isSomeChar!T || isBoolean!T) &&
+ n == T.sizeof)
+{
+ EndianSwapper!T es = void;
+ es.array = val;
+
+ version (LittleEndian)
+ immutable retval = swapEndian(es.value);
+ else
+ immutable retval = es.value;
+
+ return retval;
+}
+
+private T bigEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if (isFloatOrDouble!T && n == T.sizeof)
+{
+ version (LittleEndian)
+ return cast(T) floatEndianImpl!(n, true)(val);
+ else
+ return cast(T) floatEndianImpl!(n, false)(val);
+}
+
+
+/++
+ Converts the given value from the native endianness to little endian and
+ returns it as a $(D ubyte[n]) where $(D n) is the size of the given type.
+
+ Returning a $(D 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).
+ +/
+auto nativeToLittleEndian(T)(T val) @safe pure nothrow @nogc
+if (canSwapEndianness!T)
+{
+ return nativeToLittleEndianImpl(val);
+}
+
+///
+@safe unittest
+{
+ int i = 12345;
+ ubyte[4] swappedI = nativeToLittleEndian(i);
+ assert(i == littleEndianToNative!int(swappedI));
+
+ double d = 123.45;
+ ubyte[8] swappedD = nativeToLittleEndian(d);
+ assert(d == littleEndianToNative!double(swappedD));
+}
+
+private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc
+if (isIntegral!T || isSomeChar!T || isBoolean!T)
+{
+ EndianSwapper!T es = void;
+
+ version (BigEndian)
+ es.value = swapEndian(val);
+ else
+ es.value = val;
+
+ return es.array;
+}
+
+private auto nativeToLittleEndianImpl(T)(T val) @safe pure nothrow @nogc
+if (isFloatOrDouble!T)
+{
+ version (BigEndian)
+ return floatEndianImpl!(T, true)(val);
+ else
+ return floatEndianImpl!(T, false)(val);
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (T; AliasSeq!(bool, byte, ubyte, short, ushort, int, uint, long, ulong,
+ char, wchar, dchar/*,
+ float, double*/))
+ {
+ scope(failure) writefln("Failed type: %s", T.stringof);
+ T val;
+ const T cval;
+ immutable T ival;
+
+ //is instead of == because of NaN for floating point values.
+ assert(littleEndianToNative!T(nativeToLittleEndian(val)) is val);
+ assert(littleEndianToNative!T(nativeToLittleEndian(cval)) is cval);
+ assert(littleEndianToNative!T(nativeToLittleEndian(ival)) is ival);
+ assert(littleEndianToNative!T(nativeToLittleEndian(T.min)) == T.min);
+ assert(littleEndianToNative!T(nativeToLittleEndian(T.max)) == T.max);
+
+ static if (isSigned!T)
+ assert(littleEndianToNative!T(nativeToLittleEndian(cast(T) 0)) == 0);
+
+ static if (!is(T == bool))
+ {
+ foreach (i; 2 .. 10)
+ {
+ immutable T maxI = cast(T)(T.max / i);
+ immutable T minI = cast(T)(T.min / i);
+
+ assert(littleEndianToNative!T(nativeToLittleEndian(maxI)) == maxI);
+
+ static if (isSigned!T)
+ assert(littleEndianToNative!T(nativeToLittleEndian(minI)) == minI);
+ }
+ }
+ }
+}
+
+
+/++
+ Converts the given value from little endian to the native endianness and
+ returns it. The value is given as a $(D ubyte[n]) where $(D 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 $(D 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).
+
+ $(D 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).
+ +/
+T littleEndianToNative(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if (canSwapEndianness!T && n == T.sizeof)
+{
+ return littleEndianToNativeImpl!T(val);
+}
+
+///
+@safe unittest
+{
+ ushort i = 12345;
+ ubyte[2] swappedI = nativeToLittleEndian(i);
+ assert(i == littleEndianToNative!ushort(swappedI));
+
+ dchar c = 'D';
+ ubyte[4] swappedC = nativeToLittleEndian(c);
+ assert(c == littleEndianToNative!dchar(swappedC));
+}
+
+private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if ((isIntegral!T || isSomeChar!T || isBoolean!T) &&
+ n == T.sizeof)
+{
+ EndianSwapper!T es = void;
+ es.array = val;
+
+ version (BigEndian)
+ immutable retval = swapEndian(es.value);
+ else
+ immutable retval = es.value;
+
+ return retval;
+}
+
+private T littleEndianToNativeImpl(T, size_t n)(ubyte[n] val) @safe pure nothrow @nogc
+if (((isFloatOrDouble!T) &&
+ n == T.sizeof))
+{
+ version (BigEndian)
+ return floatEndianImpl!(n, true)(val);
+ else
+ return floatEndianImpl!(n, false)(val);
+}
+
+private auto floatEndianImpl(T, bool swap)(T val) @safe pure nothrow @nogc
+if (isFloatOrDouble!T)
+{
+ EndianSwapper!T es = void;
+ es.value = val;
+
+ static if (swap)
+ es.intValue = swapEndian(es.intValue);
+
+ return es.array;
+}
+
+private auto floatEndianImpl(size_t n, bool swap)(ubyte[n] val) @safe pure nothrow @nogc
+if (n == 4 || n == 8)
+{
+ static if (n == 4) EndianSwapper!float es = void;
+ else static if (n == 8) EndianSwapper!double es = void;
+
+ es.array = val;
+
+ static if (swap)
+ es.intValue = swapEndian(es.intValue);
+
+ return es.value;
+}
+
+private template isFloatOrDouble(T)
+{
+ enum isFloatOrDouble = isFloatingPoint!T &&
+ !is(Unqual!(FloatingPointTypeOf!T) == real);
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (T; AliasSeq!(float, double))
+ {
+ static assert(isFloatOrDouble!(T));
+ static assert(isFloatOrDouble!(const T));
+ static assert(isFloatOrDouble!(immutable T));
+ static assert(isFloatOrDouble!(shared T));
+ static assert(isFloatOrDouble!(shared(const T)));
+ static assert(isFloatOrDouble!(shared(immutable T)));
+ }
+
+ static assert(!isFloatOrDouble!(real));
+ static assert(!isFloatOrDouble!(const real));
+ static assert(!isFloatOrDouble!(immutable real));
+ static assert(!isFloatOrDouble!(shared real));
+ static assert(!isFloatOrDouble!(shared(const real)));
+ static assert(!isFloatOrDouble!(shared(immutable real)));
+}
+
+private template canSwapEndianness(T)
+{
+ enum canSwapEndianness = isIntegral!T ||
+ isSomeChar!T ||
+ isBoolean!T ||
+ isFloatOrDouble!T;
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (T; AliasSeq!(bool, ubyte, byte, ushort, short, uint, int, ulong,
+ long, char, wchar, dchar, float, double))
+ {
+ static assert(canSwapEndianness!(T));
+ static assert(canSwapEndianness!(const T));
+ static assert(canSwapEndianness!(immutable T));
+ static assert(canSwapEndianness!(shared(T)));
+ static assert(canSwapEndianness!(shared(const T)));
+ static assert(canSwapEndianness!(shared(immutable T)));
+ }
+
+ //!
+ foreach (T; AliasSeq!(real, string, wstring, dstring))
+ {
+ static assert(!canSwapEndianness!(T));
+ static assert(!canSwapEndianness!(const T));
+ static assert(!canSwapEndianness!(immutable T));
+ static assert(!canSwapEndianness!(shared(T)));
+ static assert(!canSwapEndianness!(shared(const T)));
+ static assert(!canSwapEndianness!(shared(immutable T)));
+ }
+}
+
+/++
+ Takes a range of $(D ubyte)s and converts the first $(D T.sizeof) bytes to
+ $(D T). The value returned is converted from the given endianness to the
+ native endianness. The range is not consumed.
+
+ Params:
+ T = The integral type to convert the first $(D T.sizeof) bytes to.
+ endianness = The endianness that the bytes are assumed to be in.
+ range = The range to read from.
+ 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 $(D hasSlicing!R) is $(D true).
+ +/
+
+T peek(T, Endian endianness = Endian.bigEndian, R)(R range)
+if (canSwapEndianness!T &&
+ isForwardRange!R &&
+ is(ElementType!R : const ubyte))
+{
+ static if (hasSlicing!R)
+ const ubyte[T.sizeof] bytes = range[0 .. T.sizeof];
+ else
+ {
+ ubyte[T.sizeof] bytes;
+ //Make sure that range is not consumed, even if it's a class.
+ range = range.save;
+
+ foreach (ref e; bytes)
+ {
+ e = range.front;
+ range.popFront();
+ }
+ }
+
+ static if (endianness == Endian.bigEndian)
+ return bigEndianToNative!T(bytes);
+ else
+ return littleEndianToNative!T(bytes);
+}
+
+/++ Ditto +/
+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))
+{
+ return peek!(T, endianness)(range, &index);
+}
+
+/++ Ditto +/
+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))
+{
+ assert(index);
+
+ immutable begin = *index;
+ immutable end = begin + T.sizeof;
+ const ubyte[T.sizeof] bytes = range[begin .. end];
+ *index = end;
+
+ static if (endianness == Endian.bigEndian)
+ return bigEndianToNative!T(bytes);
+ else
+ return littleEndianToNative!T(bytes);
+}
+
+///
+@system unittest
+{
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
+ assert(buffer.peek!uint() == 17110537);
+ assert(buffer.peek!ushort() == 261);
+ assert(buffer.peek!ubyte() == 1);
+
+ assert(buffer.peek!uint(2) == 369700095);
+ assert(buffer.peek!ushort(2) == 5641);
+ assert(buffer.peek!ubyte(2) == 22);
+
+ size_t index = 0;
+ assert(buffer.peek!ushort(&index) == 261);
+ assert(index == 2);
+
+ assert(buffer.peek!uint(&index) == 369700095);
+ assert(index == 6);
+
+ assert(buffer.peek!ubyte(&index) == 8);
+ assert(index == 7);
+}
+
+@system unittest
+{
+ {
+ //bool
+ ubyte[] buffer = [0, 1];
+ assert(buffer.peek!bool() == false);
+ assert(buffer.peek!bool(1) == true);
+
+ size_t index = 0;
+ assert(buffer.peek!bool(&index) == false);
+ assert(index == 1);
+
+ assert(buffer.peek!bool(&index) == true);
+ assert(index == 2);
+ }
+
+ {
+ //char (8bit)
+ ubyte[] buffer = [97, 98, 99, 100];
+ assert(buffer.peek!char() == 'a');
+ assert(buffer.peek!char(1) == 'b');
+
+ size_t index = 0;
+ assert(buffer.peek!char(&index) == 'a');
+ assert(index == 1);
+
+ assert(buffer.peek!char(&index) == 'b');
+ assert(index == 2);
+ }
+
+ {
+ //wchar (16bit - 2x ubyte)
+ ubyte[] buffer = [1, 5, 32, 29, 1, 7];
+ assert(buffer.peek!wchar() == 'ą');
+ assert(buffer.peek!wchar(2) == '”');
+ assert(buffer.peek!wchar(4) == 'ć');
+
+ size_t index = 0;
+ assert(buffer.peek!wchar(&index) == 'ą');
+ assert(index == 2);
+
+ assert(buffer.peek!wchar(&index) == '”');
+ assert(index == 4);
+
+ assert(buffer.peek!wchar(&index) == 'ć');
+ assert(index == 6);
+ }
+
+ {
+ //dchar (32bit - 4x ubyte)
+ ubyte[] buffer = [0, 0, 1, 5, 0, 0, 32, 29, 0, 0, 1, 7];
+ assert(buffer.peek!dchar() == 'ą');
+ assert(buffer.peek!dchar(4) == '”');
+ assert(buffer.peek!dchar(8) == 'ć');
+
+ size_t index = 0;
+ assert(buffer.peek!dchar(&index) == 'ą');
+ assert(index == 4);
+
+ assert(buffer.peek!dchar(&index) == '”');
+ assert(index == 8);
+
+ assert(buffer.peek!dchar(&index) == 'ć');
+ assert(index == 12);
+ }
+
+ {
+ //float (32bit - 4x ubyte)
+ ubyte[] buffer = [66, 0, 0, 0, 65, 200, 0, 0];
+ assert(buffer.peek!float()== 32.0);
+ assert(buffer.peek!float(4) == 25.0f);
+
+ size_t index = 0;
+ assert(buffer.peek!float(&index) == 32.0f);
+ assert(index == 4);
+
+ assert(buffer.peek!float(&index) == 25.0f);
+ assert(index == 8);
+ }
+
+ {
+ //double (64bit - 8x ubyte)
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+ assert(buffer.peek!double() == 32.0);
+ assert(buffer.peek!double(8) == 25.0);
+
+ size_t index = 0;
+ assert(buffer.peek!double(&index) == 32.0);
+ assert(index == 8);
+
+ assert(buffer.peek!double(&index) == 25.0);
+ assert(index == 16);
+ }
+
+ {
+ //enum
+ ubyte[] buffer = [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30];
+
+ enum Foo
+ {
+ one = 10,
+ two = 20,
+ three = 30
+ }
+
+ assert(buffer.peek!Foo() == Foo.one);
+ assert(buffer.peek!Foo(0) == Foo.one);
+ assert(buffer.peek!Foo(4) == Foo.two);
+ assert(buffer.peek!Foo(8) == Foo.three);
+
+ size_t index = 0;
+ assert(buffer.peek!Foo(&index) == Foo.one);
+ assert(index == 4);
+
+ assert(buffer.peek!Foo(&index) == Foo.two);
+ assert(index == 8);
+
+ assert(buffer.peek!Foo(&index) == Foo.three);
+ assert(index == 12);
+ }
+
+ {
+ //enum - bool
+ ubyte[] buffer = [0, 1];
+
+ enum Bool: bool
+ {
+ bfalse = false,
+ btrue = true,
+ }
+
+ assert(buffer.peek!Bool() == Bool.bfalse);
+ assert(buffer.peek!Bool(0) == Bool.bfalse);
+ assert(buffer.peek!Bool(1) == Bool.btrue);
+
+ size_t index = 0;
+ assert(buffer.peek!Bool(&index) == Bool.bfalse);
+ assert(index == 1);
+
+ assert(buffer.peek!Bool(&index) == Bool.btrue);
+ assert(index == 2);
+ }
+
+ {
+ //enum - float
+ ubyte[] buffer = [66, 0, 0, 0, 65, 200, 0, 0];
+
+ enum Float: float
+ {
+ one = 32.0f,
+ two = 25.0f
+ }
+
+ assert(buffer.peek!Float() == Float.one);
+ assert(buffer.peek!Float(0) == Float.one);
+ assert(buffer.peek!Float(4) == Float.two);
+
+ size_t index = 0;
+ assert(buffer.peek!Float(&index) == Float.one);
+ assert(index == 4);
+
+ assert(buffer.peek!Float(&index) == Float.two);
+ assert(index == 8);
+ }
+
+ {
+ //enum - double
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+
+ enum Double: double
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ assert(buffer.peek!Double() == Double.one);
+ assert(buffer.peek!Double(0) == Double.one);
+ assert(buffer.peek!Double(8) == Double.two);
+
+ size_t index = 0;
+ assert(buffer.peek!Double(&index) == Double.one);
+ assert(index == 8);
+
+ assert(buffer.peek!Double(&index) == Double.two);
+ assert(index == 16);
+ }
+
+ {
+ //enum - real
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+
+ enum Real: real
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ static assert(!__traits(compiles, buffer.peek!Real()));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 7];
+ auto range = filter!"true"(buffer);
+ assert(range.peek!uint() == 17110537);
+ assert(range.peek!ushort() == 261);
+ assert(range.peek!ubyte() == 1);
+}
+
+
+/++
+ Takes a range of $(D ubyte)s and converts the first $(D T.sizeof) bytes to
+ $(D T). The value returned is converted from the given endianness to the
+ native endianness. The $(D T.sizeof) bytes which are read are consumed from
+ the range.
+
+ Params:
+ T = The integral type to convert the first $(D T.sizeof) bytes to.
+ endianness = The endianness that the bytes are assumed to be in.
+ range = The range to read from.
+ +/
+T read(T, Endian endianness = Endian.bigEndian, R)(ref R range)
+if (canSwapEndianness!T && isInputRange!R && is(ElementType!R : const ubyte))
+{
+ static if (hasSlicing!R && is(typeof(R.init[0 .. 0]) : const(ubyte)[]))
+ {
+ const ubyte[T.sizeof] bytes = range[0 .. T.sizeof];
+ range.popFrontN(T.sizeof);
+ }
+ else
+ {
+ ubyte[T.sizeof] bytes;
+
+ foreach (ref e; bytes)
+ {
+ e = range.front;
+ range.popFront();
+ }
+ }
+
+ static if (endianness == Endian.bigEndian)
+ return bigEndianToNative!T(bytes);
+ else
+ return littleEndianToNative!T(bytes);
+}
+
+///
+@safe unittest
+{
+ import std.range.primitives : empty;
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
+ assert(buffer.length == 7);
+
+ assert(buffer.read!ushort() == 261);
+ assert(buffer.length == 5);
+
+ assert(buffer.read!uint() == 369700095);
+ assert(buffer.length == 1);
+
+ assert(buffer.read!ubyte() == 8);
+ assert(buffer.empty);
+}
+
+@safe unittest
+{
+ {
+ //bool
+ ubyte[] buffer = [0, 1];
+ assert(buffer.length == 2);
+
+ assert(buffer.read!bool() == false);
+ assert(buffer.length == 1);
+
+ assert(buffer.read!bool() == true);
+ assert(buffer.empty);
+ }
+
+ {
+ //char (8bit)
+ ubyte[] buffer = [97, 98, 99];
+ assert(buffer.length == 3);
+
+ assert(buffer.read!char() == 'a');
+ assert(buffer.length == 2);
+
+ assert(buffer.read!char() == 'b');
+ assert(buffer.length == 1);
+
+ assert(buffer.read!char() == 'c');
+ assert(buffer.empty);
+ }
+
+ {
+ //wchar (16bit - 2x ubyte)
+ ubyte[] buffer = [1, 5, 32, 29, 1, 7];
+ assert(buffer.length == 6);
+
+ assert(buffer.read!wchar() == 'ą');
+ assert(buffer.length == 4);
+
+ assert(buffer.read!wchar() == '”');
+ assert(buffer.length == 2);
+
+ assert(buffer.read!wchar() == 'ć');
+ assert(buffer.empty);
+ }
+
+ {
+ //dchar (32bit - 4x ubyte)
+ ubyte[] buffer = [0, 0, 1, 5, 0, 0, 32, 29, 0, 0, 1, 7];
+ assert(buffer.length == 12);
+
+ assert(buffer.read!dchar() == 'ą');
+ assert(buffer.length == 8);
+
+ assert(buffer.read!dchar() == '”');
+ assert(buffer.length == 4);
+
+ assert(buffer.read!dchar() == 'ć');
+ assert(buffer.empty);
+ }
+
+ {
+ //float (32bit - 4x ubyte)
+ ubyte[] buffer = [66, 0, 0, 0, 65, 200, 0, 0];
+ assert(buffer.length == 8);
+
+ assert(buffer.read!float()== 32.0);
+ assert(buffer.length == 4);
+
+ assert(buffer.read!float() == 25.0f);
+ assert(buffer.empty);
+ }
+
+ {
+ //double (64bit - 8x ubyte)
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+ assert(buffer.length == 16);
+
+ assert(buffer.read!double() == 32.0);
+ assert(buffer.length == 8);
+
+ assert(buffer.read!double() == 25.0);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - uint
+ ubyte[] buffer = [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30];
+ assert(buffer.length == 12);
+
+ enum Foo
+ {
+ one = 10,
+ two = 20,
+ three = 30
+ }
+
+ assert(buffer.read!Foo() == Foo.one);
+ assert(buffer.length == 8);
+
+ assert(buffer.read!Foo() == Foo.two);
+ assert(buffer.length == 4);
+
+ assert(buffer.read!Foo() == Foo.three);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - bool
+ ubyte[] buffer = [0, 1];
+ assert(buffer.length == 2);
+
+ enum Bool: bool
+ {
+ bfalse = false,
+ btrue = true,
+ }
+
+ assert(buffer.read!Bool() == Bool.bfalse);
+ assert(buffer.length == 1);
+
+ assert(buffer.read!Bool() == Bool.btrue);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - float
+ ubyte[] buffer = [66, 0, 0, 0, 65, 200, 0, 0];
+ assert(buffer.length == 8);
+
+ enum Float: float
+ {
+ one = 32.0f,
+ two = 25.0f
+ }
+
+ assert(buffer.read!Float() == Float.one);
+ assert(buffer.length == 4);
+
+ assert(buffer.read!Float() == Float.two);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - double
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+ assert(buffer.length == 16);
+
+ enum Double: double
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ assert(buffer.read!Double() == Double.one);
+ assert(buffer.length == 8);
+
+ assert(buffer.read!Double() == Double.two);
+ assert(buffer.empty);
+ }
+
+ {
+ //enum - real
+ ubyte[] buffer = [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0];
+
+ enum Real: real
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ static assert(!__traits(compiles, buffer.read!Real()));
+ }
+}
+
+@safe unittest
+{
+ import std.algorithm.iteration : filter;
+ ubyte[] buffer = [1, 5, 22, 9, 44, 255, 8];
+ auto range = filter!"true"(buffer);
+ assert(walkLength(range) == 7);
+
+ assert(range.read!ushort() == 261);
+ assert(walkLength(range) == 5);
+
+ assert(range.read!uint() == 369700095);
+ assert(walkLength(range) == 1);
+
+ assert(range.read!ubyte() == 8);
+ assert(range.empty);
+}
+
+// issue 17247
+@safe unittest
+{
+ struct UbyteRange
+ {
+ ubyte[] impl;
+ @property bool empty() { return impl.empty; }
+ @property ubyte front() { return impl.front; }
+ void popFront() { impl.popFront(); }
+ @property UbyteRange save() { return this; }
+
+ // N.B. support slicing but do not return ubyte[] slices.
+ UbyteRange opSlice(size_t start, size_t end)
+ {
+ return UbyteRange(impl[start .. end]);
+ }
+ @property size_t length() { return impl.length; }
+ size_t opDollar() { return impl.length; }
+ }
+ static assert(hasSlicing!UbyteRange);
+
+ auto r = UbyteRange([0x01, 0x00, 0x00, 0x00]);
+ int x = r.read!(int, Endian.littleEndian)();
+ assert(x == 1);
+}
+
+
+/++
+ Takes an integral value, converts it to the given endianness, and writes it
+ to the given range of $(D ubyte)s as a sequence of $(D T.sizeof) $(D ubyte)s
+ starting at index. $(D hasSlicing!R) must be $(D true).
+
+ Params:
+ T = The integral type to convert the first $(D T.sizeof) bytes to.
+ endianness = The endianness to _write the bytes in.
+ range = The range to _write to.
+ value = The value to _write.
+ index = The index to start writing to. If index is a pointer, then it
+ is updated to the index after the bytes read.
+ +/
+void write(T, Endian endianness = Endian.bigEndian, R)(R range, T value, size_t index)
+if (canSwapEndianness!T &&
+ isForwardRange!R &&
+ hasSlicing!R &&
+ is(ElementType!R : ubyte))
+{
+ write!(T, endianness)(range, value, &index);
+}
+
+/++ Ditto +/
+void write(T, Endian endianness = Endian.bigEndian, R)(R range, T value, size_t* index)
+if (canSwapEndianness!T &&
+ isForwardRange!R &&
+ hasSlicing!R &&
+ is(ElementType!R : ubyte))
+{
+ assert(index);
+
+ static if (endianness == Endian.bigEndian)
+ immutable bytes = nativeToBigEndian!T(value);
+ else
+ immutable bytes = nativeToLittleEndian!T(value);
+
+ immutable begin = *index;
+ immutable end = begin + T.sizeof;
+ *index = end;
+ range[begin .. end] = bytes[0 .. T.sizeof];
+}
+
+///
+@system unittest
+{
+ {
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!uint(29110231u, 0);
+ assert(buffer == [1, 188, 47, 215, 0, 0, 0, 0]);
+
+ buffer.write!ushort(927, 0);
+ assert(buffer == [3, 159, 47, 215, 0, 0, 0, 0]);
+
+ buffer.write!ubyte(42, 0);
+ assert(buffer == [42, 159, 47, 215, 0, 0, 0, 0]);
+ }
+
+ {
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0, 0];
+ buffer.write!uint(142700095u, 2);
+ assert(buffer == [0, 0, 8, 129, 110, 63, 0, 0, 0]);
+
+ buffer.write!ushort(19839, 2);
+ assert(buffer == [0, 0, 77, 127, 110, 63, 0, 0, 0]);
+
+ buffer.write!ubyte(132, 2);
+ assert(buffer == [0, 0, 132, 127, 110, 63, 0, 0, 0]);
+ }
+
+ {
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+ size_t index = 0;
+ buffer.write!ushort(261, &index);
+ assert(buffer == [1, 5, 0, 0, 0, 0, 0, 0]);
+ assert(index == 2);
+
+ buffer.write!uint(369700095u, &index);
+ assert(buffer == [1, 5, 22, 9, 44, 255, 0, 0]);
+ assert(index == 6);
+
+ buffer.write!ubyte(8, &index);
+ assert(buffer == [1, 5, 22, 9, 44, 255, 8, 0]);
+ assert(index == 7);
+ }
+}
+
+@system unittest
+{
+ {
+ //bool
+ ubyte[] buffer = [0, 0];
+
+ buffer.write!bool(false, 0);
+ assert(buffer == [0, 0]);
+
+ buffer.write!bool(true, 0);
+ assert(buffer == [1, 0]);
+
+ buffer.write!bool(true, 1);
+ assert(buffer == [1, 1]);
+
+ buffer.write!bool(false, 1);
+ assert(buffer == [1, 0]);
+
+ size_t index = 0;
+ buffer.write!bool(false, &index);
+ assert(buffer == [0, 0]);
+ assert(index == 1);
+
+ buffer.write!bool(true, &index);
+ assert(buffer == [0, 1]);
+ assert(index == 2);
+ }
+
+ {
+ //char (8bit)
+ ubyte[] buffer = [0, 0, 0];
+
+ buffer.write!char('a', 0);
+ assert(buffer == [97, 0, 0]);
+
+ buffer.write!char('b', 1);
+ assert(buffer == [97, 98, 0]);
+
+ size_t index = 0;
+ buffer.write!char('a', &index);
+ assert(buffer == [97, 98, 0]);
+ assert(index == 1);
+
+ buffer.write!char('b', &index);
+ assert(buffer == [97, 98, 0]);
+ assert(index == 2);
+
+ buffer.write!char('c', &index);
+ assert(buffer == [97, 98, 99]);
+ assert(index == 3);
+ }
+
+ {
+ //wchar (16bit - 2x ubyte)
+ ubyte[] buffer = [0, 0, 0, 0];
+
+ buffer.write!wchar('ą', 0);
+ assert(buffer == [1, 5, 0, 0]);
+
+ buffer.write!wchar('”', 2);
+ assert(buffer == [1, 5, 32, 29]);
+
+ size_t index = 0;
+ buffer.write!wchar('ć', &index);
+ assert(buffer == [1, 7, 32, 29]);
+ assert(index == 2);
+
+ buffer.write!wchar('ą', &index);
+ assert(buffer == [1, 7, 1, 5]);
+ assert(index == 4);
+ }
+
+ {
+ //dchar (32bit - 4x ubyte)
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+
+ buffer.write!dchar('ą', 0);
+ assert(buffer == [0, 0, 1, 5, 0, 0, 0, 0]);
+
+ buffer.write!dchar('”', 4);
+ assert(buffer == [0, 0, 1, 5, 0, 0, 32, 29]);
+
+ size_t index = 0;
+ buffer.write!dchar('ć', &index);
+ assert(buffer == [0, 0, 1, 7, 0, 0, 32, 29]);
+ assert(index == 4);
+
+ buffer.write!dchar('ą', &index);
+ assert(buffer == [0, 0, 1, 7, 0, 0, 1, 5]);
+ assert(index == 8);
+ }
+
+ {
+ //float (32bit - 4x ubyte)
+ ubyte[] buffer = [0, 0, 0, 0, 0, 0, 0, 0];
+
+ buffer.write!float(32.0f, 0);
+ assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!float(25.0f, 4);
+ assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
+
+ size_t index = 0;
+ buffer.write!float(25.0f, &index);
+ assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
+ assert(index == 4);
+
+ buffer.write!float(32.0f, &index);
+ assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
+ assert(index == 8);
+ }
+
+ {
+ //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);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!double(25.0, 8);
+ assert(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);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ assert(index == 8);
+
+ buffer.write!double(32.0, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+ assert(index == 16);
+ }
+
+ {
+ //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);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!Foo(Foo.two, 4);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 0]);
+
+ buffer.write!Foo(Foo.three, 8);
+ assert(buffer == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
+
+ size_t index = 0;
+ buffer.write!Foo(Foo.three, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 20, 0, 0, 0, 30]);
+ assert(index == 4);
+
+ buffer.write!Foo(Foo.one, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 30]);
+ assert(index == 8);
+
+ buffer.write!Foo(Foo.two, &index);
+ assert(buffer == [0, 0, 0, 30, 0, 0, 0, 10, 0, 0, 0, 20]);
+ assert(index == 12);
+ }
+
+ {
+ //enum - bool
+ ubyte[] buffer = [0, 0];
+
+ enum Bool: bool
+ {
+ bfalse = false,
+ btrue = true,
+ }
+
+ buffer.write!Bool(Bool.btrue, 0);
+ assert(buffer == [1, 0]);
+
+ buffer.write!Bool(Bool.btrue, 1);
+ assert(buffer == [1, 1]);
+
+ size_t index = 0;
+ buffer.write!Bool(Bool.bfalse, &index);
+ assert(buffer == [0, 1]);
+ assert(index == 1);
+
+ buffer.write!Bool(Bool.bfalse, &index);
+ assert(buffer == [0, 0]);
+ assert(index == 2);
+ }
+
+ {
+ //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);
+ assert(buffer == [66, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!Float(Float.two, 4);
+ assert(buffer == [66, 0, 0, 0, 65, 200, 0, 0]);
+
+ size_t index = 0;
+ buffer.write!Float(Float.two, &index);
+ assert(buffer == [65, 200, 0, 0, 65, 200, 0, 0]);
+ assert(index == 4);
+
+ buffer.write!Float(Float.one, &index);
+ assert(buffer == [65, 200, 0, 0, 66, 0, 0, 0]);
+ assert(index == 8);
+ }
+
+ {
+ //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);
+ assert(buffer == [64, 64, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
+
+ buffer.write!Double(Double.two, 8);
+ assert(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);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ assert(index == 8);
+
+ buffer.write!Double(Double.one, &index);
+ assert(buffer == [64, 57, 0, 0, 0, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+ assert(index == 16);
+ }
+
+ {
+ //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)));
+ }
+}
+
+
+/++
+ Takes an integral value, converts it to the given endianness, and appends
+ it to the given range of $(D ubyte)s (using $(D put)) as a sequence of
+ $(D T.sizeof) $(D ubyte)s starting at index. $(D hasSlicing!R) must be
+ $(D true).
+
+ Params:
+ T = The integral type to convert the first $(D T.sizeof) bytes to.
+ endianness = The endianness to write the bytes in.
+ range = The range to _append to.
+ value = The value to _append.
+ +/
+void append(T, Endian endianness = Endian.bigEndian, R)(R range, T value)
+if (canSwapEndianness!T && isOutputRange!(R, ubyte))
+{
+ static if (endianness == Endian.bigEndian)
+ immutable bytes = nativeToBigEndian!T(value);
+ else
+ immutable bytes = nativeToLittleEndian!T(value);
+
+ put(range, bytes[]);
+}
+
+///
+@safe unittest
+{
+ import std.array;
+ auto buffer = appender!(const ubyte[])();
+ buffer.append!ushort(261);
+ assert(buffer.data == [1, 5]);
+
+ buffer.append!uint(369700095u);
+ assert(buffer.data == [1, 5, 22, 9, 44, 255]);
+
+ buffer.append!ubyte(8);
+ assert(buffer.data == [1, 5, 22, 9, 44, 255, 8]);
+}
+
+@safe unittest
+{
+ import std.array;
+ {
+ //bool
+ auto buffer = appender!(const ubyte[])();
+
+ buffer.append!bool(true);
+ assert(buffer.data == [1]);
+
+ buffer.append!bool(false);
+ assert(buffer.data == [1, 0]);
+ }
+
+ {
+ //char wchar dchar
+ auto buffer = appender!(const ubyte[])();
+
+ buffer.append!char('a');
+ assert(buffer.data == [97]);
+
+ buffer.append!char('b');
+ assert(buffer.data == [97, 98]);
+
+ buffer.append!wchar('ą');
+ assert(buffer.data == [97, 98, 1, 5]);
+
+ buffer.append!dchar('ą');
+ assert(buffer.data == [97, 98, 1, 5, 0, 0, 1, 5]);
+ }
+
+ {
+ //float double
+ auto buffer = appender!(const ubyte[])();
+
+ buffer.append!float(32.0f);
+ assert(buffer.data == [66, 0, 0, 0]);
+
+ buffer.append!double(32.0);
+ assert(buffer.data == [66, 0, 0, 0, 64, 64, 0, 0, 0, 0, 0, 0]);
+ }
+
+ {
+ //enum
+ auto buffer = appender!(const ubyte[])();
+
+ enum Foo
+ {
+ one = 10,
+ two = 20,
+ three = 30
+ }
+
+ buffer.append!Foo(Foo.one);
+ assert(buffer.data == [0, 0, 0, 10]);
+
+ buffer.append!Foo(Foo.two);
+ assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20]);
+
+ buffer.append!Foo(Foo.three);
+ assert(buffer.data == [0, 0, 0, 10, 0, 0, 0, 20, 0, 0, 0, 30]);
+ }
+
+ {
+ //enum - bool
+ auto buffer = appender!(const ubyte[])();
+
+ enum Bool: bool
+ {
+ bfalse = false,
+ btrue = true,
+ }
+
+ buffer.append!Bool(Bool.btrue);
+ assert(buffer.data == [1]);
+
+ buffer.append!Bool(Bool.bfalse);
+ assert(buffer.data == [1, 0]);
+
+ buffer.append!Bool(Bool.btrue);
+ assert(buffer.data == [1, 0, 1]);
+ }
+
+ {
+ //enum - float
+ auto buffer = appender!(const ubyte[])();
+
+ enum Float: float
+ {
+ one = 32.0f,
+ two = 25.0f
+ }
+
+ buffer.append!Float(Float.one);
+ assert(buffer.data == [66, 0, 0, 0]);
+
+ buffer.append!Float(Float.two);
+ assert(buffer.data == [66, 0, 0, 0, 65, 200, 0, 0]);
+ }
+
+ {
+ //enum - double
+ auto buffer = appender!(const ubyte[])();
+
+ enum Double: double
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ buffer.append!Double(Double.one);
+ assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0]);
+
+ buffer.append!Double(Double.two);
+ assert(buffer.data == [64, 64, 0, 0, 0, 0, 0, 0, 64, 57, 0, 0, 0, 0, 0, 0]);
+ }
+
+ {
+ //enum - real
+ auto buffer = appender!(const ubyte[])();
+
+ enum Real: real
+ {
+ one = 32.0,
+ two = 25.0
+ }
+
+ static assert(!__traits(compiles, buffer.append!Real(Real.one)));
+ }
+}
+
+@system unittest
+{
+ import std.array;
+ import std.format : format;
+ import std.meta;
+ foreach (endianness; AliasSeq!(Endian.bigEndian, Endian.littleEndian))
+ {
+ auto toWrite = appender!(ubyte[])();
+ alias Types = AliasSeq!(uint, int, long, ulong, short, ubyte, ushort, byte, uint);
+ ulong[] values = [42, -11, long.max, 1098911981329L, 16, 255, 19012, 2, 17];
+ assert(Types.length == values.length);
+
+ size_t index = 0;
+ size_t length = 0;
+ foreach (T; Types)
+ {
+ toWrite.append!(T, endianness)(cast(T) values[index++]);
+ length += T.sizeof;
+ }
+
+ auto toRead = toWrite.data;
+ assert(toRead.length == length);
+
+ index = 0;
+ foreach (T; Types)
+ {
+ assert(toRead.peek!(T, endianness)() == values[index], format("Failed Index: %s", index));
+ assert(toRead.peek!(T, endianness)(0) == values[index], format("Failed Index: %s", index));
+ assert(toRead.length == length,
+ format("Failed Index [%s], Actual Length: %s", index, toRead.length));
+ assert(toRead.read!(T, endianness)() == values[index], format("Failed Index: %s", index));
+ length -= T.sizeof;
+ assert(toRead.length == length,
+ format("Failed Index [%s], Actual Length: %s", index, toRead.length));
+ ++index;
+ }
+ assert(toRead.empty);
+ }
+}
+
+/**
+Counts the number of set bits in the binary representation of $(D value).
+For signed integers, the sign bit is included in the count.
+*/
+private uint countBitsSet(T)(T value) @nogc pure nothrow
+if (isIntegral!T)
+{
+ // http://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
+ static if (T.sizeof == 8)
+ {
+ T c = value - ((value >> 1) & 0x55555555_55555555);
+ c = ((c >> 2) & 0x33333333_33333333) + (c & 0x33333333_33333333);
+ c = ((c >> 4) + c) & 0x0F0F0F0F_0F0F0F0F;
+ c = ((c >> 8) + c) & 0x00FF00FF_00FF00FF;
+ c = ((c >> 16) + c) & 0x0000FFFF_0000FFFF;
+ c = ((c >> 32) + c) & 0x00000000_FFFFFFFF;
+ }
+ else static if (T.sizeof == 4)
+ {
+ T c = value - ((value >> 1) & 0x55555555);
+ c = ((c >> 2) & 0x33333333) + (c & 0x33333333);
+ c = ((c >> 4) + c) & 0x0F0F0F0F;
+ c = ((c >> 8) + c) & 0x00FF00FF;
+ c = ((c >> 16) + c) & 0x0000FFFF;
+ }
+ else static if (T.sizeof == 2)
+ {
+ uint c = value - ((value >> 1) & 0x5555);
+ c = ((c >> 2) & 0x3333) + (c & 0X3333);
+ c = ((c >> 4) + c) & 0x0F0F;
+ c = ((c >> 8) + c) & 0x00FF;
+ }
+ else static if (T.sizeof == 1)
+ {
+ uint c = value - ((value >> 1) & 0x55);
+ c = ((c >> 2) & 0x33) + (c & 0X33);
+ c = ((c >> 4) + c) & 0x0F;
+ }
+ else
+ {
+ static assert(false, "countBitsSet only supports 1, 2, 4, or 8 byte sized integers.");
+ }
+ return cast(uint) c;
+}
+
+@safe unittest
+{
+ assert(countBitsSet(1) == 1);
+ assert(countBitsSet(0) == 0);
+ assert(countBitsSet(int.min) == 1);
+ assert(countBitsSet(uint.max) == 32);
+}
+
+@safe unittest
+{
+ import std.meta;
+ foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {
+ assert(countBitsSet(cast(T) 0) == 0);
+ assert(countBitsSet(cast(T) 1) == 1);
+ assert(countBitsSet(cast(T) 2) == 1);
+ assert(countBitsSet(cast(T) 3) == 2);
+ assert(countBitsSet(cast(T) 4) == 1);
+ assert(countBitsSet(cast(T) 5) == 2);
+ assert(countBitsSet(cast(T) 127) == 7);
+ static if (isSigned!T)
+ {
+ assert(countBitsSet(cast(T)-1) == 8 * T.sizeof);
+ assert(countBitsSet(T.min) == 1);
+ }
+ else
+ {
+ assert(countBitsSet(T.max) == 8 * T.sizeof);
+ }
+ }
+ assert(countBitsSet(1_000_000) == 7);
+ foreach (i; 0 .. 63)
+ assert(countBitsSet(1UL << i) == 1);
+}
+
+private struct BitsSet(T)
+{
+ static assert(T.sizeof <= 8, "bitsSet assumes T is no more than 64-bit.");
+
+@nogc pure nothrow:
+
+ this(T value, size_t startIndex = 0)
+ {
+ _value = value;
+ // Further calculation is only valid and needed when the range is non-empty.
+ if (!_value)
+ return;
+
+ import core.bitop : bsf;
+ immutable trailingZerosCount = bsf(value);
+ _value >>>= trailingZerosCount;
+ _index = startIndex + trailingZerosCount;
+ }
+
+ @property size_t front()
+ {
+ return _index;
+ }
+
+ @property bool empty() const
+ {
+ return !_value;
+ }
+
+ void popFront()
+ {
+ assert(_value, "Cannot call popFront on empty range.");
+
+ _value >>>= 1;
+ // Further calculation is only valid and needed when the range is non-empty.
+ if (!_value)
+ return;
+
+ import core.bitop : bsf;
+ immutable trailingZerosCount = bsf(_value);
+ _value >>>= trailingZerosCount;
+ _index += trailingZerosCount + 1;
+ }
+
+ @property auto save()
+ {
+ return this;
+ }
+
+ @property size_t length()
+ {
+ return countBitsSet(_value);
+ }
+
+ private T _value;
+ private size_t _index;
+}
+
+/**
+Range that iterates the indices of the set bits in $(D value).
+Index 0 corresponds to the least significant bit.
+For signed integers, the highest index corresponds to the sign bit.
+*/
+auto bitsSet(T)(T value) @nogc pure nothrow
+if (isIntegral!T)
+{
+ return BitsSet!T(value);
+}
+
+///
+@safe unittest
+{
+ 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]));
+}
+
+@safe unittest
+{
+ import std.algorithm.comparison : equal;
+ import std.range : iota;
+
+ import std.meta;
+ foreach (T; AliasSeq!(byte, ubyte, short, ushort, int, uint, long, ulong))
+ {
+ assert(bitsSet(cast(T) 0).empty);
+ assert(bitsSet(cast(T) 1).equal([0]));
+ assert(bitsSet(cast(T) 2).equal([1]));
+ assert(bitsSet(cast(T) 3).equal([0, 1]));
+ assert(bitsSet(cast(T) 4).equal([2]));
+ assert(bitsSet(cast(T) 5).equal([0, 2]));
+ assert(bitsSet(cast(T) 127).equal(iota(7)));
+ static if (isSigned!T)
+ {
+ assert(bitsSet(cast(T)-1).equal(iota(8 * T.sizeof)));
+ assert(bitsSet(T.min).equal([8 * T.sizeof - 1]));
+ }
+ else
+ {
+ assert(bitsSet(T.max).equal(iota(8 * T.sizeof)));
+ }
+ }
+ assert(bitsSet(1_000_000).equal([6, 9, 14, 16, 17, 18, 19]));
+ foreach (i; 0 .. 63)
+ assert(bitsSet(1UL << i).equal([i]));
+}